你有没有看着你的网络层,然后心想,"肯定有更简洁的方法来做这个吧?"
欢迎关注我的微信公众号:OpenFlutter,感恩

上个月,我盯着我们 Android 应用里超过 2000 行的 Retrofit 样板代码,头都大了:各种自定义的转换器,散落在多个文件里的拦截器,还有多到让我头晕的接口方法。就在这时,我决定试一试 Ktor。
结果呢?我的网络代码减少了一半,而且我重新爱上了处理 HTTP 请求。
为什么我开始质疑 Retrofit
别误会我------多年来,Retrofit 一直是 Android 网络请求的黄金标准。但随着我们应用的壮大,它的痛点也越来越多:
- 样板代码泛滥: 每个 API 端点都需要一个自己的接口方法。
- 复杂的序列化: 每个非标准的响应都需要一个自定义的转换器。
- 测试噩梦: Mock Retrofit 服务就像在解谜题。
kotlin
// ApiService.kt
interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") userId: String): Response<User>
@POST("users")
suspend fun createUser(@Body user: CreateUserRequest): Response<User>
@PUT("users/{id}")
suspend fun updateUser(@Path("id") userId: String, @Body user: UpdateUserRequest): Response<User>
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") userId: String): Response<Unit>
}
// NetworkModule.kt
@Module
class NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(AuthInterceptor())
.addInterceptor(LoggingInterceptor())
.connectTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
// Repository.kt
class UserRepository @Inject constructor(
private val apiService: ApiService
) {
suspend fun getUser(userId: String): Result<User> {
return try {
val response = apiService.getUser(userId)
if (response.isSuccessful) {
response.body()?.let { Result.success(it) }
?: Result.failure(Exception("Empty response"))
} else {
Result.failure(Exception("HTTP ${response.code()}"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
}
为了基本的CRUD操作都已经 60+ 多行了。而且我们甚至没有加入错误处理、重试逻辑或自定义Header。

Ktor 登场:Kotlin 原生的替代方案
Ktor 采取了一种根本不同的方法。它没有使用注解和代码生成,而是拥抱了函数式编程 和 DSL 语法。
下面是用 Ktor 重写的相同功能:
kotlin
// ApiClient.kt
class ApiClient {
private val client = HttpClient(Android) {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.INFO
}
install(Auth) {
bearer {
loadTokens {
BearerTokens(getAccessToken(), getRefreshToken())
}
}
}
defaultRequest {
url("https://api.example.com/")
contentType(ContentType.Application.Json)
}
install(HttpTimeout) {
requestTimeoutMillis = 30000
}
}
suspend inline fun <reified T> get(path: String): T {
return client.get(path).body()
}
suspend inline fun <reified T> post(path: String, body: Any): T {
return client.post(path) {
setBody(body)
}.body()
}
suspend inline fun <reified T> put(path: String, body: Any): T {
return client.put(path) {
setBody(body)
}.body()
}
suspend fun delete(path: String) {
client.delete(path)
}
}
// Repository.kt
class UserRepository(private val api: ApiClient) {
suspend fun getUser(userId: String): Result<User> = runCatching {
api.get<User>("users/$userId")
}
suspend fun createUser(user: CreateUserRequest): Result<User> = runCatching {
api.post<User>("users", user)
}
suspend fun updateUser(userId: String, user: UpdateUserRequest): Result<User> = runCatching {
api.put<User>("users/$userId", user)
}
suspend fun deleteUser(userId: String): Result<Unit> = runCatching {
api.delete("users/$userId")
}
}
总共只有 30 行代码。我们去掉了:
- 接口定义
- 自定义转换器工厂
- 手动响应处理
- 依赖注入的样板代码
数字不会说谎
在迁移了我们整个网络层之后,下面是发生的变化:
指标 (Metric) | Retrofit | Ktor | 提升 (Improvement) |
---|---|---|---|
代码行数 (Lines of Code) | 2,156 | 1,078 | 减少 50% |
API 接口方法数 (API Interface Methods) | 47 | 0 | 消除 100% |
构建时间 (干净构建) (Build Time (clean)) | 2.3 分钟 | 1.9 分钟 | 快 17% |
网络测试覆盖率 (Network Test Coverage) | 67% | 89% | 增加 22% |

为什么 Ktor 如此简洁?
1. 实体化泛型(Reified Generics)= 不再需要响应包装器
使用 Retrofit 时,每个响应都需要明确的类型定义:
kotlin
suspend fun getUser(@Path("id") userId: String): Response<User>
Ktor 的实体化泛型让你无需使用包装器:
kotlin
suspend inline fun <reified T> get(path: String): T = client.get(path).body()
2. 内置序列化
Retrofit 需要单独的转换器工厂。Ktor 开箱即用地处理了 JSON 序列化:
kotlin
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
3. 函数式错误处理
不用到处检查 response.isSuccessful()
,而是把调用代码用 runCatching
包起来:
kotlin
suspend fun getUser(id: String): Result<User> = runCatching {
api.get<User>("users/$id")
}
迁移过程(比你想象的要简单)
下面是我进行迁移的方法,并且没有搞砸一切:
第一步:添加 Ktor 依赖
kotlin
implementation("io.ktor:ktor-client-android:2.3.7" )
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
第二步:创建并排实现
从一个仓库开始,逐步迁移端点:
kotlin
class UserRepository(
private val retrofitService: ApiService, // 保持旧的
private val ktorClient: ApiClient // 增加新的
) {
suspend fun getUser(userId: String): Result<User> {
return if (useKtor) {
runCatching { ktorClient.get<User>("users/$userId") }
} else {
// 回滚至Retrofit
retrofitService.getUser(userId).toResult()
}
}
}
第三步:彻底地测试一番
使用功能标志(feature flags)来逐步开放基于Ktor的endpoints:
kotlin
val useKtor = BuildConfig.DEBUG || featureFlags.isKtorEnabled()

Ktor是否任何时候都适合你?
Ktor 并非在所有情况下都是完美的:
- 经验参差不齐的大型团队: Retrofit 基于注解的方法对 Java 开发者来说更熟悉。
- 现有的代码生成工具: 如果你正在使用 OpenAPI 生成器,Retrofit 有更好的工具支持。
- 遗留 Java 项目: Ktor 的"Kotlin 优先"设计与 Java 的兼容性不佳。
但如果你正在用 Kotlin 构建现代 Android 应用,它带来的开发体验提升是巨大的。
结论
在使用 Ktor 在生产环境中三个月后,我可以自信地说,它改变了我对网络代码的看法。样板代码的减少不仅仅是行数变少------它关乎认知负荷。
当我需要添加一个新端点时,我不再需要:
- 定义一个接口方法
- 处理响应包装器
- 编写自定义转换器
- Mock 复杂的服务接口
我只需要直接进行 HTTP 调用。
是否值得从 Retrofit 迁移? 如果你正在开始一个新项目,那绝对值得。对于现有应用,迁移的努力会很快得到回报------特别是如果你正被堆积如山的网络样板代码所淹没。