Retrofit + Kotlin 协程(Android 实战教程)
这是 Android 开发里最主流的网络请求方案:
Retrofit + Coroutines + MVVM
现代 Android 项目基本都这么写。
这一篇会从:
- Retrofit 基础
- suspend 网络请求
- 协程切线程
- MVVM 实战
- 错误处理
- Flow 配合
- 封装最佳实践
一路讲到项目级写法。
一、Retrofit 是什么?
Retrofit 是 Square 出的网络请求库。
作用:把 HTTP API 变成 Kotlin 接口
| 以前 | Retrofit 之后 |
|---|---|
| HttpURLConnection / OkHttp | api.getUser() |
| 非常麻烦 | 直接像调用本地函数 |
二、Retrofit 为什么和协程绝配?
以前 Retrofit:
Call.enqueue()回调地狱:
api.getUser().enqueue(...)协程之后:
valuser=api.getUser()像同步代码。但底层仍然是异步。
这就是suspend + Retrofit的威力。
三、添加依赖
// Retrofitimplementation"com.squareup.retrofit2:retrofit:2.11.0"implementation"com.squareup.retrofit2:converter-gson:2.11.0"// 协程implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"// ViewModelimplementation"androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0"implementation"androidx.lifecycle:lifecycle-runtime-ktx:2.8.0"四、Retrofit 基础配置
五、创建 Data 类
dataclassUser(valid:Int,valname:String)六、创建 API 接口
这是 Retrofit 核心。
interfaceApiService{@GET("user")suspendfungetUser():User}重点:suspend
Retrofit 会自动支持协程。
七、创建 Retrofit
objectRetrofitManager{valapi:ApiServicebylazy{Retrofit.Builder().baseUrl("https://example.com/").addConverterFactory(GsonConverterFactory.create()).build().create(ApiService::class.java)}}八、第一个协程网络请求
ViewModel
classUserViewModel:ViewModel(){funloadUser(){viewModelScope.launch{valuser=RetrofitManager.api.getUser()println(user.name)}}}九、为什么 suspend 不需要 enqueue?
| 以前 | 现在 |
|---|---|
enqueue(callback) | suspend fun |
Retrofit 内部已经帮你做了:
- 异步线程
Continuation.resume()
这就是协程适配器。
十、Retrofit + Continuation 原理
你写:
suspendfungetUser():UserRetrofit 底层其实拿到了:Continuation<User>
网络返回后:continuation.resume(user),协程恢复。
十一、为什么不会阻塞主线程?
很多人疑惑:
viewModelScope.launch{api.getUser()// 不是在 Main 线程吗?}为什么不卡 UI?
因为:Retrofit 内部自动切到 OkHttp 线程池,网络完成后再恢复协程。
所以:你看起来像同步,实际上是异步。
十二、withContext 什么时候需要?
Retrofit 请求本身
不需要withContext(IO),因为 Retrofit 已经异步。
但 JSON 大解析需要
valresult=withContext(Dispatchers.Default){parseBigJson()}数据库操作
withContext(Dispatchers.IO)十三、POST 请求
API
interfaceApiService{@POST("login")suspendfunlogin(@Bodybody:LoginRequest):LoginResponse}Request
dataclassLoginRequest(valusername:String,valpassword:String)十四、Query 参数
@GET("user")suspendfungetUser(@Query("id")id:Int):User// 请求:/user?id=1十五、Path 参数
@GET("user/{id}")suspendfungetUser(@Path("id")id:Int):User// 请求:/user/1十六、错误处理(非常重要)
很多人项目里直接崩。正确写法:
viewModelScope.launch{try{valuser=api.getUser()}catch(e:Exception){e.printStackTrace()}}十七、为什么会抛异常?
Retrofit 协程:网络失败会直接throw Exception,而不是onFailure()。
十八、项目级错误封装(推荐)
ResultState
sealedclassResultState<outT>{dataclassSuccess<T>(valdata:T):ResultState<T>()dataclassError(valmsg:String):ResultState<Nothing>()objectLoading:ResultState<Nothing>()}十九、统一请求封装
suspendfun<T>safeApiCall(apiCall:suspend()->T):ResultState<T>{returntry{ResultState.Success(apiCall())}catch(e:Exception){ResultState.Error(e.message?:"未知错误")}}二十、使用方式
viewModelScope.launch{when(valresult=safeApiCall{api.getUser()}){isResultState.Success->{// 处理成功}isResultState.Error->{// 处理错误}else->{}}}二十一、Repository 层(MVVM核心)
现代 Android必须分层。
二十二、Repository
classUserRepository{suspendfungetUser():User{returnRetrofitManager.api.getUser()}}二十三、ViewModel
classUserViewModel:ViewModel(){privatevalrepository=UserRepository()funload(){viewModelScope.launch{valuser=repository.getUser()}}}二十四、为什么需要 Repository?
因为 ViewModel 不应该直接操作 Retrofit,否则:
- 耦合严重
- 不好测试
- 难维护
二十五、Flow + Retrofit(现代方案)
这是现在最推荐的。
Repository
classUserRepository{fungetUser()=flow{emit(ResultState.Loading)try{valuser=api.getUser()emit(ResultState.Success(user))}catch(e:Exception){emit(ResultState.Error("请求失败"))}}}ViewModel
funloadUser(){viewModelScope.launch{repository.getUser().collect{// 处理状态}}}二十六、Flow 的优势
比 suspend 更适合:
- UI状态
- Loading
- 连续数据
- 重试
- 分页
二十七、Retrofit + OkHttp
Retrofit 底层其实是OkHttp。
二十八、添加日志拦截器
依赖
implementation"com.squareup.okhttp3:logging-interceptor:4.12.0"配置
vallogging=HttpLoggingInterceptor()logging.level=HttpLoggingInterceptor.Level.BODY二十九、OkHttpClient
valclient=OkHttpClient.Builder().addInterceptor(logging).build()三十、Retrofit 使用 client
Retrofit.Builder().client(client)三十一、拦截器(项目必会)
Token 拦截器
classTokenInterceptor:Interceptor{overridefunintercept(chain:Interceptor.Chain):Response{valrequest=chain.request().newBuilder().addHeader("token","xxx").build()returnchain.proceed(request)}}三十二、超时设置
OkHttpClient.Builder().connectTimeout(10,TimeUnit.SECONDS).readTimeout(10,TimeUnit.SECONDS)三十三、协程取消与 Retrofit
这是高频面试题。
当协程 cancel 时,Retrofit 会自动取消网络请求。
因为:Retrofit 协程和 Job 已关联。
三十四、真正的项目结构(推荐)
ui/ viewmodel/ repository/ network/ model/三十五、标准企业级结构
| 层级 | 职责 |
|---|---|
| ApiService | 定义网络接口 |
| RetrofitManager | Retrofit 配置管理 |
| Repository | 数据仓储,隔离网络层 |
| ViewModel | 业务逻辑,UI状态管理 |
| UI | Activity / Fragment / Compose |
三十六、Compose + Retrofit
在 Jetpack Compose 中:
LaunchedEffect(Unit){viewModel.load()}ViewModel:viewModelScope.launch
这是现代 Android 官方方案。
三十七、Retrofit 常见面试题
1. suspend 为什么不阻塞?
因为:Retrofit 底层OkHttp异步+Continuation恢复
2. Retrofit 为什么不需要 withContext(IO)?
因为:Retrofit 已经异步
3. Retrofit 底层是谁?
Retrofit ↓ OkHttp4. 协程取消为什么能取消请求?
因为:Coroutine Job关联OkHttp Call.cancel()
5. suspend 本质是什么?
编译器:Continuation + 状态机
三十八、现代 Android 最推荐写法
ViewModel
classUserViewModel:ViewModel(){valuiState=MutableStateFlow<ResultState<User>>(ResultState.Loading)funloadUser(){viewModelScope.launch{uiState.value=ResultState.Loading uiState.value=try{ResultState.Success(api.getUser())}catch(e:Exception){ResultState.Error("失败")}}}}三十九、真正的大脑模型
以后看到suspend fun getUser(),你脑子里应该自动出现:
Retrofit ↓ OkHttp异步请求 ↓ Continuation挂起 ↓ 网络返回 ↓ resume恢复协程 ↓ 继续执行四十、总结
| 组件 | 作用 |
|---|---|
| Retrofit | HTTP → Kotlin 接口 |
| suspend | 协程挂起 + 异步 |
| OkHttp | 底层网络执行 |
| Repository | 隔离网络层 |
| ViewModel | 状态管理与业务逻辑 |
| StateFlow / Flow | UI状态响应 |
| ResultState | 统一错误处理 |
四十一、企业级最佳实践(非常重要)
不要这样
Activity->Retrofit要这样
UI ↓ ViewModel ↓ Repository ↓ Retrofit最后一句(真正理解 Retrofit + 协程)
Retrofit + 协程真正厉害的地方:
不是“代码变短”。
而是:
把异步回调变成了同步思维
底层:
Continuation + 状态机 + OkHttp异步上层:
像写同步代码一样写异步这就是现代 Android 网络架构的核心。