OkHttp + Retrofit 协作原理

"请求到底怎么流动",再拆 Retrofit 和 OkHttp 的源码协作,重点看 OkHttp 的责任链模式,最后聊从 RxJava 切到 Kotlin 协程时怎么做、阵痛在哪里、共性是什么。

一句话结论

Retrofit 负责把"接口方法"翻译成一次 HTTP 调用,OkHttp 负责真正执行网络请求。Retrofit 像一个声明式 API 翻译器,OkHttp 像一台可插拔的网络执行引擎。

text 复制代码
你写的接口方法
    ↓ Retrofit 动态代理
解析注解、参数、返回类型
    ↓ ServiceMethod
生成 OkHttp Request
    ↓ OkHttpCall
交给 OkHttp RealCall
    ↓ RealInterceptorChain
责任链拦截器逐层处理
    ↓ 网络 I/O
拿到 Response
    ↓ Converter
转成业务对象
    ↓ CallAdapter
适配成 Call / RxJava / suspend / Flow 包装

1. 为什么 Retrofit 和 OkHttp 经常一起出现

Retrofit 解决什么

你不想每个接口都手写 URL、参数拼接、JSON 解析、错误映射,所以 Retrofit 允许你这样声明:

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Long): User
}

它关心的是:

  • 这个方法是什么 HTTP method?
  • URL 怎么拼?
  • 参数从哪里来?
  • Body 怎么序列化?
  • Response 怎么反序列化?
  • 返回值要适配成什么形式?

OkHttp 解决什么

OkHttp 关心的是:

  • 请求怎么排队?
  • 连接怎么复用?
  • 缓存怎么处理?
  • 重试和重定向怎么做?
  • 拦截器怎么执行?
  • Socket 怎么读写?

两者分工

框架 角色 更像什么
Retrofit 声明式接口层 翻译器 / 适配层
OkHttp HTTP 执行层 网络引擎
Converter 数据转换层 JSON 转换器
CallAdapter 返回类型适配层 异步模型适配器

2. Retrofit 创建接口对象:动态代理

你写的代码

kotlin 复制代码
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttpClient)
    .addConverterFactory(MoshiConverterFactory.create())
    .build()

val api = retrofit.create(UserApi::class.java)

源码原理

Retrofit 的 create() 会给接口创建一个动态代理。你调用 api.getUser(1) 时,并不是直接进入某个真实实现类,而是进入代理对象的 InvocationHandler

伪代码:

kotlin 复制代码
fun <T> create(service: Class<T>): T {
    validateServiceInterface(service)

    return Proxy.newProxyInstance(
        service.classLoader,
        arrayOf(service)
    ) { proxy, method, args ->
        val serviceMethod = loadServiceMethod(method)
        serviceMethod.invoke(proxy, args)
    } as T
}

为什么这么实现

如果不用动态代理,你就要为每个接口写实现类:

kotlin 复制代码
class UserApiImpl : UserApi {
    override suspend fun getUser(id: Long): User {
        // 手写 URL、Request、Response 解析
    }
}

动态代理的好处是:

  • 业务只写接口,不写样板实现。
  • Retrofit 可以统一拦截所有接口方法调用。
  • 注解解析和请求构建逻辑集中在框架内部。
  • 后续支持 Call、RxJava、协程,只需要扩展适配层。

3. Retrofit 如何解析一个接口方法

接口方法

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(
        @Path("id") id: Long,
        @Query("detail") detail: Boolean
    ): User
}

Retrofit 要解析的信息

text 复制代码
方法级注解:
    @GET("users/{id}") -> HTTP method + relativeUrl
参数注解:
    @Path("id") -> 替换路径
    @Query("detail") -> 拼 query
返回类型:
    suspend fun ... : User -> 协程返回 User
转换器:
    ResponseBody -> User
调用适配器:
    OkHttpCall -> suspend 返回

ServiceMethod

Retrofit 会把一个 Java/Kotlin 方法解析成 ServiceMethod。它可以理解为"接口方法的执行说明书"。

伪代码:

kotlin 复制代码
class ServiceMethod(
    val requestFactory: RequestFactory,
    val callFactory: Call.Factory,
    val responseConverter: Converter<ResponseBody, Any>,
    val callAdapter: CallAdapter<Any, Any>
) {
    fun invoke(args: Array<Any?>): Any {
        val request = requestFactory.create(args)
        val call = OkHttpCall(callFactory, request, responseConverter)
        return callAdapter.adapt(call)
    }
}

为什么要缓存 ServiceMethod

同一个接口方法会被调用很多次。如果每次都反射解析注解,成本太高。Retrofit 会缓存解析结果。

text 复制代码
第一次调用 getUser:
    反射解析注解
    创建 ServiceMethod
    放入缓存
后续调用 getUser:
    直接从缓存取 ServiceMethod

好处:

  • 减少反射成本。
  • 每次调用只关心参数值。
  • 框架结构更清晰:解析一次,执行多次。

4. Converter:ResponseBody 如何变成业务对象

常见配置

kotlin 复制代码
Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create())
    .build()

原理

OkHttp 返回的是 ResponseBody。业务要的是 UserList<User>ApiResult<User>

Converter 的职责就是类型转换:

text 复制代码
ResponseBody:
    {"id":1,"name":"Tom"}
Converter:
    JSON -> User
业务层:
    User(id=1, name="Tom")

伪代码:

kotlin 复制代码
interface Converter<F, T> {
    fun convert(value: F): T
}

class JsonUserConverter : Converter<ResponseBody, User> {
    override fun convert(value: ResponseBody): User {
        val json = value.string()
        return jsonParser.fromJson(json, User::class.java)
    }
}

好处

  • Retrofit 不绑定具体 JSON 框架。
  • 可以替换 Gson、Moshi、Kotlinx Serialization。
  • 请求 Body 和响应 Body 的转换逻辑可插拔。

5. CallAdapter:返回类型如何适配

同一个 HTTP 请求,不同返回方式

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    fun getUserCall(@Path("id") id: Long): Call<User>

    @GET("users/{id}")
    fun getUserSingle(@Path("id") id: Long): Single<User>

    @GET("users/{id}")
    suspend fun getUserSuspend(@Path("id") id: Long): User
}

底层都可以是一套 HTTP 调用,但外层返回模型不同。

原理

text 复制代码
OkHttpCall<User>
    ↓ CallAdapter
Call<User>
或 Single<User>
或 suspend 的挂起结果

伪代码:

kotlin 复制代码
interface CallAdapter<R, T> {
    fun responseType(): Type
    fun adapt(call: Call<R>): T
}

好处

  • Retrofit 核心不需要硬编码所有异步框架。
  • RxJava、Guava、Java8、协程都可以通过适配层接入。
  • 同一套接口注解可以迁移到不同异步模型。

6. OkHttp 的请求执行入口

Retrofit 最终交给 OkHttp

Retrofit 内部会创建 OkHttp 的 Request,然后调用 OkHttp 的 Call.Factory

kotlin 复制代码
val request: Request = requestFactory.create(args)
val call: okhttp3.Call = okHttpClient.newCall(request)

同步和异步

kotlin 复制代码
call.execute()   // 同步执行
call.enqueue(callback) // 异步执行

在 Android 主线程不能直接 execute() 做网络请求。Retrofit 的协程和 RxJava 适配通常会帮你把异步模型包装得更舒服,但底层仍然是 OkHttp Call。


7. OkHttp Dispatcher:请求如何排队

Dispatcher 做什么

OkHttp 的 Dispatcher 管理异步请求队列,包括:

  • 正在运行的异步请求。
  • 等待中的异步请求。
  • 同域名最大并发数。
  • 全局最大并发数。

伪代码:

kotlin 复制代码
class Dispatcher {
    val readyAsyncCalls = ArrayDeque<AsyncCall>()
    val runningAsyncCalls = ArrayDeque<AsyncCall>()

    fun enqueue(call: AsyncCall) {
        if (canRunMoreCalls()) {
            runningAsyncCalls += call
            executorService.execute(call)
        } else {
            readyAsyncCalls += call
        }
    }

    fun finished(call: AsyncCall) {
        runningAsyncCalls -= call
        promoteAndExecute()
    }
}

好处

  • 避免无限制创建网络请求。
  • 控制同一 host 的并发。
  • 请求完成后自动推进等待队列。
  • 网络层调度和业务层调用解耦。

8. OkHttp 责任链模式总览

OkHttp 最经典的设计就是拦截器责任链。一次请求会按顺序经过多个拦截器,每个拦截器可以:

  • 修改 Request。
  • 继续调用下一个拦截器。
  • 拿到 Response 后再加工。
  • 在特殊情况下直接返回 Response。
  • 抛出异常终止链路。

简化链路

text 复制代码
Application Interceptors
    ↓
RetryAndFollowUpInterceptor
    ↓
BridgeInterceptor
    ↓
CacheInterceptor
    ↓
ConnectInterceptor
    ↓
Network Interceptors
    ↓
CallServerInterceptor

RealInterceptorChain 伪代码

kotlin 复制代码
class RealInterceptorChain(
    private val interceptors: List<Interceptor>,
    private val index: Int,
    private val request: Request
) : Interceptor.Chain {

    override fun proceed(request: Request): Response {
        val next = RealInterceptorChain(
            interceptors = interceptors,
            index = index + 1,
            request = request
        )

        val interceptor = interceptors[index]
        return interceptor.intercept(next)
    }
}

Interceptor 伪代码

kotlin 复制代码
class HeaderInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val oldRequest = chain.request()
        val newRequest = oldRequest.newBuilder()
            .addHeader("token", tokenProvider.token())
            .build()

        val response = chain.proceed(newRequest)
        return response
    }
}

9. 责任链模式为什么适合网络框架

网络请求天然是流水线

一次 HTTP 请求不是"发出去,拿回来"这么简单,中间有很多阶段:

text 复制代码
补 Header
处理 Cookie
检查缓存
失败重试
重定向
建立连接
写请求体
读响应体
打印日志
统一错误处理

如果全写在一个大方法里,会变成很难维护的巨型流程。

责任链的好处

好处 说明
职责单一 每个拦截器只处理一种横切逻辑
顺序清晰 链表式执行,前后关系明确
可插拔 业务可以添加自定义拦截器
可复用 Header、日志、认证、缓存逻辑能独立复用
可测试 每个拦截器可以单独测试
双向处理 Request 往下走,Response 往回走

双向处理模型

text 复制代码
请求方向:
    A -> B -> C -> Server
响应方向:
    Server -> C -> B -> A

所以一个日志拦截器可以在 proceed() 前记录请求,在 proceed() 后记录响应。


10. OkHttp 内置拦截器做什么

RetryAndFollowUpInterceptor

负责失败恢复、重试、重定向等后续请求。

text 复制代码
发送请求
如果需要重定向:
    构造新 Request
    再走一次链路
如果连接失败且允许重试:
    重试
否则:
    返回响应或抛异常

BridgeInterceptor

在用户请求和网络请求之间做桥接,例如添加一些协议层 Header、处理 gzip。

text 复制代码
用户 Request:
    只关心业务 Header
Bridge:
    补充 Host、Connection、Accept-Encoding、Cookie 等
网络 Response:
    如果 gzip,解压给上层

CacheInterceptor

根据缓存策略决定:

  • 直接用缓存。
  • 直接走网络。
  • 条件缓存验证。
  • 合并缓存和网络响应。

ConnectInterceptor

负责找到可用连接,建立或复用连接。

text 复制代码
从连接池找连接
如果可复用:
    使用已有连接
否则:
    建立新连接

CallServerInterceptor

真正把请求写到服务器,并读取响应。

text 复制代码
写请求头
写请求体
读响应头
读响应体
返回 Response

11. Application Interceptor 和 Network Interceptor

Application Interceptor

添加方式:

kotlin 复制代码
OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor())
    .build()

特点:

  • 更靠近业务层。
  • 通常只执行一次逻辑。
  • 看不到中间重定向的每一次网络请求细节。
  • 适合统一 Header、日志、业务错误处理。

Network Interceptor

添加方式:

kotlin 复制代码
OkHttpClient.Builder()
    .addNetworkInterceptor(NetworkLogInterceptor())
    .build()

特点:

  • 更靠近真实网络。
  • 能观察每次网络交换。
  • 可以访问连接信息。
  • 适合底层网络诊断。

选择建议

场景 推荐
加 token Application Interceptor
统一业务 header Application Interceptor
打印业务请求日志 Application Interceptor
观察重定向过程 Network Interceptor
诊断连接和网络层 Network Interceptor

12. Retrofit + OkHttp 完整协作伪代码

kotlin 复制代码
interface Api {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Long): User
}

val okHttp = OkHttpClient.Builder()
    .addInterceptor(AuthInterceptor())
    .addInterceptor(LogInterceptor())
    .build()

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(okHttp)
    .addConverterFactory(MoshiConverterFactory.create())
    .build()

val api = retrofit.create(Api::class.java)
val user = api.getUser(1L)

执行过程:

text 复制代码
api.getUser(1)
    ↓
动态代理拦截方法调用
    ↓
读取 getUser 方法对应的 ServiceMethod
    ↓
根据 @GET、@Path 创建 Request
    ↓
OkHttpClient.newCall(request)
    ↓
RealCall.enqueue/execute
    ↓
RealInterceptorChain.proceed
    ↓
内置和自定义拦截器逐个处理
    ↓
CallServerInterceptor 访问网络
    ↓
ResponseBody 返回给 Retrofit
    ↓
Converter 转成 User
    ↓
suspend 函数恢复,返回 User

13. 老项目里的 RxJava + Retrofit

典型接口

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    fun getUser(@Path("id") id: Long): Single<User>
}

典型调用

kotlin 复制代码
api.getUser(1L)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { user -> showUser(user) },
        { error -> showError(error) }
    )

这套写法解决了什么

  • 把网络请求放到 IO 线程。
  • 把 UI 更新切回主线程。
  • 用链式操作符组合异步任务。
  • Disposable 取消订阅。

常见问题

kotlin 复制代码
private val disposables = CompositeDisposable()

override fun onDestroy() {
    disposables.clear()
}

如果忘记清理,页面销毁后请求还可能回调 UI,引发生命周期问题。


14. RxJava 配合 Retrofit 的源码思路

Retrofit 支持 RxJava 靠的是 CallAdapter。

text 复制代码
接口返回 Single<User>
    ↓
Retrofit 查找 RxJava CallAdapter
    ↓
把 OkHttpCall<User> 包装成 Single<User>
    ↓
subscribe 时触发网络请求

伪代码:

kotlin 复制代码
class RxJavaCallAdapter<R> : CallAdapter<R, Single<R>> {
    override fun adapt(call: Call<R>): Single<R> {
        return Single.create { emitter ->
            call.enqueue(object : Callback<R> {
                override fun onResponse(call: Call<R>, response: Response<R>) {
                    if (response.isSuccessful) {
                        emitter.onSuccess(response.body()!!)
                    } else {
                        emitter.onError(HttpException(response))
                    }
                }

                override fun onFailure(call: Call<R>, t: Throwable) {
                    emitter.onError(t)
                }
            })

            emitter.setCancellable {
                call.cancel()
            }
        }
    }
}

好处

  • Retrofit 不直接依赖业务如何订阅。
  • RxJava 的线程切换、组合、错误处理继续生效。
  • Disposable.dispose() 可以映射到底层 Call.cancel()

15. Kotlin 协程版 Retrofit

接口写法

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Long): User
}

调用:

kotlin 复制代码
viewModelScope.launch {
    try {
        val user = api.getUser(1L)
        _uiState.value = UiState.Content(user)
    } catch (e: Exception) {
        _uiState.value = UiState.Error("加载失败")
    }
}

Retrofit 如何支持 suspend

Retrofit 会识别接口方法是否是 Kotlin suspend 方法。suspend 方法在 JVM 层会多一个 Continuation 参数,Retrofit 通过这个特征进入协程适配逻辑。

伪代码:

text 复制代码
解析方法返回类型:
    如果最后一个参数是 Continuation:
        这是 suspend 方法
        走 SuspendForBody 或 SuspendForResponse
否则:
        走普通 CallAdapter

协程挂起流程:

text 复制代码
suspend fun getUser()
    ↓
Retrofit 创建 OkHttpCall
    ↓
enqueue 异步请求
    ↓
当前协程挂起
    ↓
OkHttp 回调成功
    ↓
Converter 转 User
    ↓
Continuation.resume(user)
    ↓
协程从挂起点继续执行

好处

  • 调用代码像同步一样直。
  • 取消跟协程 Job 绑定。
  • ViewModel 生命周期结束时可自动取消。
  • 错误处理可以回到普通 try/catch

16. 从 RxJava 切到协程怎么做

第一步:接口返回类型迁移

RxJava:

kotlin 复制代码
@GET("users/{id}")
fun getUser(@Path("id") id: Long): Single<User>

协程:

kotlin 复制代码
@GET("users/{id}")
suspend fun getUser(@Path("id") id: Long): User

如果需要拿到 HTTP 状态码:

kotlin 复制代码
@GET("users/{id}")
suspend fun getUserResponse(@Path("id") id: Long): Response<User>

第二步:调用层迁移

RxJava:

kotlin 复制代码
disposables += api.getUser(id)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(::showUser, ::showError)

协程:

kotlin 复制代码
viewModelScope.launch {
    runCatching {
        api.getUser(id)
    }.onSuccess { user ->
        _uiState.value = UiState.Content(user)
    }.onFailure { error ->
        _uiState.value = UiState.Error(error.message ?: "加载失败")
    }
}

第三步:线程切换迁移

RxJava:

kotlin 复制代码
api.getUsers()
    .subscribeOn(Schedulers.io())
    .map { users -> heavyMap(users) }
    .observeOn(AndroidSchedulers.mainThread())

协程:

kotlin 复制代码
viewModelScope.launch {
    val users = api.getUsers()
    val uiModels = withContext(Dispatchers.Default) {
        heavyMap(users)
    }
    _uiState.value = UiState.Content(uiModels)
}

Retrofit 的 suspend 网络等待本身不需要你手动 withContext(Dispatchers.IO) 包一层,但本地文件、数据库、大计算仍然要切到合适 Dispatcher。

第四步:生命周期迁移

RxJava:

kotlin 复制代码
override fun onDestroy() {
    disposables.clear()
}

协程:

kotlin 复制代码
class UserViewModel : ViewModel() {
    fun load() {
        viewModelScope.launch {
            api.getUser(1L)
        }
    }
}

ViewModel 清除时,viewModelScope 会取消内部协程。


17. 阵痛期会痛在哪里

1. 返回类型思维变化

RxJava 习惯:

kotlin 复制代码
fun getUser(): Single<User>

协程习惯:

kotlin 复制代码
suspend fun getUser(): User

痛点:以前"异步"体现在返回类型里,现在体现在函数修饰符和调用上下文里。

2. 操作符迁移

RxJava:

kotlin 复制代码
api.search(keyword)
    .debounce(300, TimeUnit.MILLISECONDS)
    .switchMap { api.search(it) }

Flow:

kotlin 复制代码
keywordFlow
    .debounce(300)
    .flatMapLatest { api.searchFlow(it) }

痛点:名字相似但语义细节不完全一样,团队需要重新统一写法。

3. 错误处理变化

RxJava:

kotlin 复制代码
.onErrorReturn { emptyList() }

协程:

kotlin 复制代码
val users = runCatching {
    repository.getUsers()
}.getOrElse {
    emptyList()
}

Flow:

kotlin 复制代码
flow.catch { emit(emptyList()) }

痛点:普通 suspend 用 try/catch,Flow 用 catch,并发任务还要理解异常传播。

4. 取消语义变化

RxJava 取消:

kotlin 复制代码
disposable.dispose()

协程取消:

kotlin 复制代码
job.cancel()

痛点:协程取消是协作式的,不能随便吞掉 CancellationException

5. 测试方式变化

RxJava 常见:

kotlin 复制代码
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }

协程常见:

kotlin 复制代码
@Test
fun load() = runTest {
    viewModel.load()
    advanceUntilIdle()
    assertTrue(viewModel.uiState.value is UiState.Content)
}

痛点:要理解测试 Dispatcher、虚拟时间、Flow 收集。


18. RxJava 和协程有什么共性

共性 RxJava Kotlin 协程
异步任务 SingleObservable suspendDeferred
流式数据 ObservableFlowable Flow
线程切换 subscribeOnobserveOn withContext、Dispatcher
取消 Disposable Job
错误处理 onError... try/catchcatch
组合任务 zipflatMap async/awaitcombineflatMapLatest
生命周期绑定 AutoDispose / 手动 clear viewModelScoperepeatOnLifecycle

关键理解

RxJava 和协程都在解决异步编排问题。区别是:

  • RxJava 更像"数据流操作库"。
  • 协程更像"语言级异步模型"。
  • Flow 补上了协程中的流式数据能力。

19. 迁移推荐策略

不推荐一刀切

不要把全项目所有接口一次性从 RxJava 改成 suspend。风险太高。

推荐分层迁移

text 复制代码
新接口:
    直接用 suspend
老接口:
    保持 RxJava,按模块迁移
Repository:
    对外逐步暴露 suspend
ViewModel:
    新页面用 viewModelScope
UI:
    状态统一 StateFlow

过渡期适配

如果底层还是 RxJava,可以先在 Repository 转成 suspend。

伪代码:

kotlin 复制代码
suspend fun <T : Any> Single<T>.awaitValue(): T =
    suspendCancellableCoroutine { continuation ->
        val disposable = subscribe(
            { value -> continuation.resume(value) },
            { error -> continuation.resumeWithException(error) }
        )

        continuation.invokeOnCancellation {
            disposable.dispose()
        }
    }

这样 ViewModel 可以先切到协程,而数据源暂时不动。


20. 推荐的 Android 落地写法

Api

kotlin 复制代码
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Long): UserDto
}

Repository

kotlin 复制代码
class UserRepository(
    private val api: UserApi
) {
    suspend fun getUser(id: Long): AppResult<User> {
        return try {
            val user = api.getUser(id).toDomain()
            AppResult.Success(user)
        } catch (e: CancellationException) {
            throw e
        } catch (e: HttpException) {
            AppResult.Failure("服务异常:${e.code()}")
        } catch (e: IOException) {
            AppResult.Failure("网络异常")
        } catch (e: Exception) {
            AppResult.Failure("未知异常")
        }
    }
}

ViewModel

kotlin 复制代码
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
    val uiState: StateFlow<UserUiState> = _uiState

    fun loadUser(id: Long) {
        viewModelScope.launch {
            _uiState.value = UserUiState.Loading
            _uiState.value = when (val result = repository.getUser(id)) {
                is AppResult.Success -> UserUiState.Content(result.data)
                is AppResult.Failure -> UserUiState.Error(result.message)
            }
        }
    }
}

Fragment 收集

kotlin 复制代码
viewLifecycleOwner.lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { state ->
            render(state)
        }
    }
}

21. 业务拦截器实战

Token 拦截器

kotlin 复制代码
class AuthInterceptor(
    private val tokenProvider: TokenProvider
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val token = tokenProvider.token()
        val request = chain.request()
            .newBuilder()
            .addHeader("Authorization", "Bearer $token")
            .build()

        return chain.proceed(request)
    }
}

统一错误拦截器

kotlin 复制代码
class BusinessErrorInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response = chain.proceed(chain.request())

        if (response.code == 401) {
            // 通知登录过期,或触发刷新 token 流程
        }

        return response
    }
}

注意

拦截器里不要随便读取 response.body.string(),因为 body 只能消费一次。日志框架通常会复制或缓冲内容再重新构造 ResponseBody。


22. 面试怎么讲

可以按这个顺序:

text 复制代码
1. Retrofit 通过动态代理创建接口实现。
2. 调用接口方法时,解析或读取缓存的 ServiceMethod。
3. ServiceMethod 根据注解和参数生成 OkHttp Request。
4. OkHttpClient.newCall 创建 RealCall。
5. RealCall 通过 Dispatcher 管理异步请求。
6. 真正执行时进入 RealInterceptorChain。
7. 请求依次经过自定义拦截器和内置拦截器。
8. CallServerInterceptor 完成网络读写。
9. ResponseBody 回到 Retrofit,由 Converter 转成业务对象。
10. 最后由 CallAdapter 适配为 Call、RxJava 类型或协程 suspend 返回。

责任链模式一句话

OkHttp 用责任链把网络请求拆成多个可插拔阶段,每个拦截器只处理自己的职责,并通过 chain.proceed() 把请求交给下一个节点,响应再沿原路返回。

RxJava 到协程一句话

从 RxJava 切到协程,本质是从"返回一个异步流对象,然后订阅它",变成"在协程作用域中调用 suspend 函数,让异步代码写得像顺序代码",而 Flow 负责承接持续数据流场景。


23. 源码入口

以下源码路径按 2026-09-08 查询官方仓库整理:

主题 源码入口
Retrofit 动态代理 Retrofit.java
Retrofit 方法解析 ServiceMethod.java
Retrofit suspend 适配 HttpServiceMethod.java
Retrofit 协程扩展 KotlinExtensions.kt
OkHttp RealCall RealCall.kt
OkHttp 责任链 RealInterceptorChain.kt
OkHttp Dispatcher Dispatcher.kt
Android 协程建议 Android Kotlin Coroutines

24. 总结

Retrofit 和 OkHttp 的协作,本质是"声明式接口层"和"网络执行引擎"的组合。Retrofit 通过动态代理、注解解析、Converter、CallAdapter 把接口方法变成 HTTP 调用;OkHttp 通过 Dispatcher 和责任链拦截器把请求稳定、高扩展地执行出去。

RxJava 到协程的迁移,不是简单替换几个返回类型,而是异步思维的调整:从订阅链转向结构化并发,从 Disposable 转向 Job,从 observeOn 转向生命周期感知的状态收集。阵痛期会有,但它们解决的问题高度相似。真正迁移顺了以后,ViewModel 里的网络请求会更直观,生命周期也更容易收住。

相关推荐
降临-max2 天前
接口自动化(TestNG+OKHttp)
软件测试·okhttp·自动化·testng
吴声子夜歌3 天前
Java扩展——OkHttp
java·开发语言·okhttp
额鹅恶饿呃3 天前
Asynchronous JavaScript And XML(异步的JS和XML )
okhttp
泡海椒4 天前
JQuick-Curl 性能分析:并发场景下的性能表现与调优,第三方接口调用不只要快写也要稳跑
java·开发语言·okhttp
泡海椒4 天前
JQuick-Curl 二次开发:自定义解析器、扩展点开发指南,如何把框架能力真正变成团队资产
java·开发语言·okhttp·maven
摇滚侠5 天前
《SpringBoot 3:入门与应用实战》第 9 章 跨域问题 阅读笔记 27
spring boot·笔记·okhttp
刘广睿7 天前
断点续传与分片下载是怎么实现的?Range 请求在视频下载中的工程实践
okhttp·ffmpeg·音视频·下载·python3.11
泡海椒12 天前
JQuick-Curl vs RestTemplate / OkHttp / OpenFeign:框架选型对比,谁更适合第三方接口调用?
okhttp
Co_Hui15 天前
OkHttp 原理
okhttp