Retrofit + Kotlin 协程(Android 实战教程)

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:

kotlin 复制代码
Call.enqueue()

回调地狱:

kotlin 复制代码
api.getUser().enqueue(...)

协程之后:

kotlin 复制代码
val user = api.getUser()

像同步代码。但底层仍然是异步。

这就是 suspend + Retrofit 的威力。


三、添加依赖

kotlin 复制代码
// Retrofit
implementation "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"

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.0"

四、Retrofit 基础配置


五、创建 Data 类

kotlin 复制代码
data class User(
    val id: Int,
    val name: String
)

六、创建 API 接口

这是 Retrofit 核心。

kotlin 复制代码
interface ApiService {
    @GET("user")
    suspend fun getUser(): User
}

重点: suspend

Retrofit 会自动支持协程。


七、创建 Retrofit

kotlin 复制代码
object RetrofitManager {
    val api: ApiService by lazy {
        Retrofit.Builder()
            .baseUrl("https://example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

八、第一个协程网络请求

ViewModel

kotlin 复制代码
class UserViewModel : ViewModel() {
    fun loadUser() {
        viewModelScope.launch {
            val user = RetrofitManager.api.getUser()
            println(user.name)
        }
    }
}

九、为什么 suspend 不需要 enqueue?

以前 现在
enqueue(callback) suspend fun

Retrofit 内部已经帮你做了:

  • 异步线程
  • Continuation.resume()

这就是协程适配器。


十、Retrofit + Continuation 原理

你写:

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

Retrofit 底层其实拿到了: Continuation<User>

网络返回后:continuation.resume(user),协程恢复。


十一、为什么不会阻塞主线程?

很多人疑惑:

kotlin 复制代码
viewModelScope.launch {
    api.getUser() // 不是在 Main 线程吗?
}

为什么不卡 UI?

因为:Retrofit 内部自动切到 OkHttp 线程池,网络完成后再恢复协程。

所以:你看起来像同步,实际上是异步。


十二、withContext 什么时候需要?

Retrofit 请求本身

不需要 withContext(IO),因为 Retrofit 已经异步。

但 JSON 大解析需要

kotlin 复制代码
val result = withContext(Dispatchers.Default) {
    parseBigJson()
}

数据库操作

kotlin 复制代码
withContext(Dispatchers.IO)

十三、POST 请求

API

kotlin 复制代码
interface ApiService {
    @POST("login")
    suspend fun login(@Body body: LoginRequest): LoginResponse
}

Request

kotlin 复制代码
data class LoginRequest(
    val username: String,
    val password: String
)

十四、Query 参数

kotlin 复制代码
@GET("user")
suspend fun getUser(@Query("id") id: Int): User

// 请求:/user?id=1

十五、Path 参数

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

// 请求:/user/1

十六、错误处理(非常重要)

很多人项目里直接崩。正确写法:

kotlin 复制代码
viewModelScope.launch {
    try {
        val user = api.getUser()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

十七、为什么会抛异常?

Retrofit 协程:网络失败会直接 throw Exception,而不是 onFailure()


十八、项目级错误封装(推荐)

ResultState

kotlin 复制代码
sealed class ResultState<out T> {
    data class Success<T>(val data: T) : ResultState<T>()
    data class Error(val msg: String) : ResultState<Nothing>()
    object Loading : ResultState<Nothing>()
}

十九、统一请求封装

kotlin 复制代码
suspend fun <T> safeApiCall(apiCall: suspend () -> T): ResultState<T> {
    return try {
        ResultState.Success(apiCall())
    } catch (e: Exception) {
        ResultState.Error(e.message ?: "未知错误")
    }
}

二十、使用方式

kotlin 复制代码
viewModelScope.launch {
    when (val result = safeApiCall { api.getUser() }) {
        is ResultState.Success -> {
            // 处理成功
        }
        is ResultState.Error -> {
            // 处理错误
        }
        else -> {}
    }
}

二十一、Repository 层(MVVM核心)

现代 Android 必须分层


二十二、Repository

kotlin 复制代码
class UserRepository {
    suspend fun getUser(): User {
        return RetrofitManager.api.getUser()
    }
}

二十三、ViewModel

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

    fun load() {
        viewModelScope.launch {
            val user = repository.getUser()
        }
    }
}

二十四、为什么需要 Repository?

因为 ViewModel 不应该直接操作 Retrofit,否则:

  • 耦合严重
  • 不好测试
  • 难维护

二十五、Flow + Retrofit(现代方案)

这是现在最推荐的。

Repository

kotlin 复制代码
class UserRepository {
    fun getUser() = flow {
        emit(ResultState.Loading)
        try {
            val user = api.getUser()
            emit(ResultState.Success(user))
        } catch (e: Exception) {
            emit(ResultState.Error("请求失败"))
        }
    }
}

ViewModel

kotlin 复制代码
fun loadUser() {
    viewModelScope.launch {
        repository.getUser().collect {
            // 处理状态
        }
    }
}

二十六、Flow 的优势

比 suspend 更适合:

  • UI状态
  • Loading
  • 连续数据
  • 重试
  • 分页

二十七、Retrofit + OkHttp

Retrofit 底层其实是 OkHttp


二十八、添加日志拦截器

依赖

kotlin 复制代码
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"

配置

kotlin 复制代码
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY

二十九、OkHttpClient

kotlin 复制代码
val client = OkHttpClient.Builder()
    .addInterceptor(logging)
    .build()

三十、Retrofit 使用 client

kotlin 复制代码
Retrofit.Builder()
    .client(client)

三十一、拦截器(项目必会)

Token 拦截器

kotlin 复制代码
class TokenInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
            .newBuilder()
            .addHeader("token", "xxx")
            .build()
        return chain.proceed(request)
    }
}

三十二、超时设置

kotlin 复制代码
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 中:

kotlin 复制代码
LaunchedEffect(Unit) {
    viewModel.load()
}

ViewModel:viewModelScope.launch

这是现代 Android 官方方案。


三十七、Retrofit 常见面试题

1. suspend 为什么不阻塞?

因为:Retrofit 底层 OkHttp异步 + Continuation恢复

2. Retrofit 为什么不需要 withContext(IO)?

因为:Retrofit 已经异步

3. Retrofit 底层是谁?

复制代码
Retrofit
    ↓
OkHttp

4. 协程取消为什么能取消请求?

因为:Coroutine Job 关联 OkHttp Call.cancel()

5. suspend 本质是什么?

编译器:Continuation + 状态机


三十八、现代 Android 最推荐写法

ViewModel

kotlin 复制代码
class UserViewModel : ViewModel() {
    val uiState = MutableStateFlow<ResultState<User>>(ResultState.Loading)

    fun loadUser() {
        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 统一错误处理

四十一、企业级最佳实践(非常重要)

不要这样

c 复制代码
Activity -> Retrofit

要这样

bash 复制代码
UI
 ↓
ViewModel
 ↓
Repository
 ↓
Retrofit

最后一句(真正理解 Retrofit + 协程)

Retrofit + 协程真正厉害的地方:

不是"代码变短"。

而是:

把异步回调变成了同步思维

底层:

bash 复制代码
Continuation
+
状态机
+
OkHttp异步

上层:

bash 复制代码
像写同步代码一样写异步

这就是现代 Android 网络架构的核心。

相关推荐
大炮筒8 小时前
COCOS2DX4.0CPPWIN移植安卓踩坑总结
android
qq_4228286210 小时前
android图形学之SurfaceControl和Surface的关系 五
android·开发语言·python
tongyiixiaohuang12 小时前
轻易云平台助力快麦数据入库MySQL
android·数据库·mysql
JohnnyDeng9415 小时前
Android 包体积优化:R8/ProGuard 深度配置
android
qq_4523962315 小时前
第六篇:《JMeter逻辑控制器:循环、条件和交替执行》
android·java·jmeter
高林雨露16 小时前
kotlin by 和 = 的区别在于【属性委托】和直【接赋值】的差异
kotlin
cwzqf16 小时前
Jectpack Compose项目组件代码分享(1):分页加载组件
android
@北海怪兽17 小时前
SQL常见函数整理 _ STRING_AGG()
android·数据库·sql
鹏晨互联18 小时前
【Compose vs XML:边框内外间距的实现对比】
android·xml