我用 Ktor 替换了 Retrofit-我的网络代码减少了一半

你有没有看着你的网络层,然后心想,"肯定有更简洁的方法来做这个吧?"

欢迎关注我的微信公众号: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 迁移? 如果你正在开始一个新项目,那绝对值得。对于现有应用,迁移的努力会很快得到回报------特别是如果你正被堆积如山的网络样板代码所淹没。

相关推荐
晴空雨8 分钟前
React Media 深度解析:从使用到 window.matchMedia API 详解
前端·react.js
一个有故事的男同学8 分钟前
React性能优化全景图:从问题发现到解决方案
前端
探码科技10 分钟前
2025年20+超实用技术文档工具清单推荐
前端
Juchecar13 分钟前
Vue 3 推荐选择组合式 API 风格(附录与选项式的代码对比)
前端·vue.js
uncleTom66616 分钟前
# 从零实现一个Vue 3通用建议选择器组件:设计思路与最佳实践
前端·vue.js
影i16 分钟前
iOS WebView 异步跳转解决方案
前端
Nicholas6817 分钟前
flutter滚动视图之ScrollController源码解析(三)
前端
爪洼守门员17 分钟前
安装electron报错的解决方法
前端·javascript·electron
web前端进阶者24 分钟前
electron-vite_19配置环境变量
前端·javascript·electron
棒棒的唐27 分钟前
nodejs安装后 使用npm 只能在cmd 里使用 ,但是不能在poowershell使用,只能用npm.cmd
前端·npm·node.js