首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >[Android 从零到一] Retrofit + OkHttp 网络请求

[Android 从零到一] Retrofit + OkHttp 网络请求

原创
作者头像
hunter android
发布2026-06-18 11:10:44
发布2026-06-18 11:10:44
420
举报

背景

几乎没有 Android 应用不需要联网。列表加载、用户登录、上传图片、推送消息——这些功能的背后都有一个共同的基石:HTTP 网络请求。早期的 Android 开发常用 HttpURLConnection 或 Apache HttpClient,写起来样板代码多、线程管理繁琐、解析响应还得手写 JSON 解析逻辑。

直到 Square 公司先后开源了 OkHttp 和 Retrofit,局面才发生根本性改变。OkHttp 是一个高效、可定制的 HTTP 客户端,处理连接池、缓存、重定向、拦截器等底层细节。Retrofit 则在 OkHttp 之上做了一层声明式抽象,把 HTTP API 变成 Java/Kotlin 接口,开发者只需定义接口方法加几个注解,Retrofit 自动完成请求构造、参数序列化、响应反序列化。

一句话总结:OkHttp 管网络传输,Retrofit 管接口封装,两者配合是 Android 网络层的事实标准。

核心概念

Retrofit 接口声明

Retrofit 的核心思想是"接口即 API"。你不需要手动拼接 URL、设置请求头、构造 RequestBody,只需要:

代码语言:javascript
复制
interface ApiService {

    @GET("users/{userId}")

    suspend fun getUser(@Path("userId") id: String): Response<User>

}

`@GET` 指定请求方式,`@Path` 替代路径参数,suspend 标记为挂起函数后可直接在协程中调用。Retrofit 在运行时会动态生成接口的实现类,你拿到的就是一个"活的"接口实例。

OkHttp 的作用

每次 Retrofit 请求最终都会交给 OkHttp 去真正发出去。OkHttp 负责连接建立、TLS 握手、请求排队、连接复用、超时控制。通过拦截器机制,你可以在请求发出前和响应返回后做很多事情:添加公共请求头、打印日志、重试失败请求、缓存策略等。

数据转换器

Retrofit 本身不关心响应体是什么格式,需要配合 Converter 来转换。最常见的是 Moshi 或 Gson:

代码语言:javascript
复制
Retrofit.Builder()

    .addConverterFactory(MoshiConverterFactory.create())

这样 Retrofit 就会自动把 JSON 响应反序列化成你指定的 Kotlin 数据类。

代码实战(Kotlin)

1. 添加依赖

代码语言:javascript
复制
// build.gradle.kts (module)

dependencies {

    implementation("com.squareup.retrofit2:retrofit:2.11.0")

    implementation("com.squareup.retrofit2:converter-moshi:2.11.0")

    implementation("com.squareup.okhttp3:okhttp:4.12.0")

    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

    implementation("com.squareup.moshi:moshi-kotlin:1.15.1")

}

2. 定义数据类和接口

代码语言:javascript
复制
import com.squareup.moshi.JsonClass

import retrofit2.Response

import retrofit2.http.GET

import retrofit2.http.Query



@JsonClass(generateAdapter = true)

data class User(

    val id: Long,

    val name: String,

    val email: String

)



@JsonClass(generateAdapter = true)

data class UserListResponse(

    val data: List<User>,

    val total: Int

)



interface ApiService {

    @GET("api/users")

    suspend fun getUsers(

        @Query("page") page: Int = 1,

        @Query("limit") limit: Int = 20

    ): Response<UserListResponse>

}

3. 配置 Retrofit 客户端

代码语言:javascript
复制
import okhttp3.OkHttpClient

import okhttp3.logging.HttpLoggingInterceptor

import retrofit2.Retrofit

import retrofit2.converter.moshi.MoshiConverterFactory

import java.util.concurrent.TimeUnit



object RetrofitClient {



    private const val BASE_URL = "https://api.example.com/"



    private val okHttpClient: OkHttpClient by lazy {

        val logging = HttpLoggingInterceptor().apply {

            level = HttpLoggingInterceptor.Level.BODY

        }

        OkHttpClient.Builder()

            .connectTimeout(30, TimeUnit.SECONDS)

            .readTimeout(30, TimeUnit.SECONDS)

            .addInterceptor { chain ->

                val request = chain.request().newBuilder()

                    .addHeader("Authorization", "Bearer YOUR_TOKEN")

                    .addHeader("Accept", "application/json")

                    .build()

                chain.proceed(request)

            }

            .addInterceptor(logging)

            .build()

    }



    val apiService: ApiService by lazy {

        Retrofit.Builder()

            .baseUrl(BASE_URL)

            .client(okHttpClient)

            .addConverterFactory(MoshiConverterFactory.create())

            .build()

            .create(ApiService::class.java)

    }

}

4. ViewModel 中调用

代码语言:javascript
复制
import androidx.lifecycle.LiveData

import androidx.lifecycle.MutableLiveData

import androidx.lifecycle.ViewModel

import androidx.lifecycle.viewModelScope

import kotlinx.coroutines.launch



class UserListViewModel : ViewModel() {



    private val _users = MutableLiveData<List<User>>()

    val users: LiveData<List<User>> = _users



    private val _error = MutableLiveData<String>()

    val error: LiveData<String> = _error



    fun loadUsers(page: Int = 1) {

        viewModelScope.launch {

            try {

                val response = RetrofitClient.apiService.getUsers(page)

                if (response.isSuccessful) {

                    _users.value = response.body()?.data ?: emptyList()

                } else {

                    _error.value = "请求失败:${response.code()}"

                }

            } catch (e: Exception) {

                _error.value = "网络异常:${e.message}"

            }

        }

    }

}

核心要点:用 `viewModelScope.launch` 发起协程,Retrofit 的 suspend 函数自动在后台线程执行网络请求,不需要手动切线程。`Response<T>` 可以同时拿到状态码和解析后的 body,方便做错误处理。

避坑指南

**baseUrl 要以斜杠结尾。** 如果写 `"https://api.example.com"` 而不是 `"https://api.example.com/"`,接口中的 `/api/users` 会被解析为完整的 `api/users`,导致路径拼接异常。这是新人最容易踩的坑。

**不要每次请求都创建 Retrofit 实例。** Retrofit 和 OkHttpClient 中有连接池、线程池等重量资源,创建开销大。应该用单例或依赖注入框架(如 Hilt)管理。

**区分 suspend 和 Call。** 用 suspend 函数时异常会被抛出,需要在 try-catch 中捕获;用 `Call<T>` 时则通过 `enqueue` 回调处理。协程项目建议统一用 suspend。

**拦截器顺序很重要。** 先添加的拦截器在请求链的外层先执行、内层后执行。日志拦截器一般放在最后,这样能记录最终发出的完整请求,包括前面拦截器添加的请求头。

**不要忘记关闭响应体。** 虽然 Retrofit 处理了大部分情况,但在用 `ResponseBody` 时,记得 close。另外,`Response<T>` 的 body 在协程场景下会自动管理,普通场景下需要留意。

**生产环境关闭详细日志。** `HttpLoggingInterceptor.Level.BODY` 会打印请求体和响应体,可能泄露敏感信息(密钥、用户数据)。上线前建议切换到 `Level.NONE` 或仅保留 `Level.HEADERS`。

总结

Retrofit + OkHttp 把网络请求从"手写样板代码"变成了"声明接口 + 配置客户端"的简洁范式。你定义数据类描述响应结构,用注解描述 API 形态,OkHttp 保证传输质量,Retrofit 负责胶水代码。配合 Kotlin 协程的 suspend 函数,整个调用链简洁、可读、不易出错。

掌握了这套工具,再结合之前学过的 ViewModel + LiveData,就能搭建出一条完整的数据通道:网络请求(Retrofit)→ 状态管理(ViewModel)→ UI 更新(LiveData)。这正是 MVVM 架构中最经典的数据流向。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 核心概念
    • Retrofit 接口声明
    • OkHttp 的作用
    • 数据转换器
  • 代码实战(Kotlin)
    • 1. 添加依赖
    • 2. 定义数据类和接口
    • 3. 配置 Retrofit 客户端
    • 4. ViewModel 中调用
  • 避坑指南
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档