選Retrofit還是Ktor?手把手教你做決定
每次新建Android項(xiàng)目,選網(wǎng)絡(luò)庫(kù)就像在奶茶店點(diǎn)單——Retrofit是經(jīng)典珍珠奶茶穩(wěn)中求勝,Ktor是新出的芝士葡萄鮮果茶讓人躍躍欲試。
- 當(dāng)你急著對(duì)接API時(shí),哪個(gè)能三分鐘搞定?
- 當(dāng)產(chǎn)品經(jīng)理突發(fā)奇想要加WebSocket時(shí),哪個(gè)能優(yōu)雅接招?
- 當(dāng)老板說(shuō)要搞跨平臺(tái)開發(fā)時(shí),哪個(gè)能讓你少掉頭發(fā)?
舉個(gè)栗子
Retrofit基礎(chǔ)用法
// 1. 定義接口(就像點(diǎn)菜單)
interface NewsApiService {
@GET("news/latest")
suspendfun getLatestNews(): Response<List<News>>
}
// 2. 創(chuàng)建Retrofit實(shí)例(配菜過(guò)程)
val retrofit = Retrofit.Builder()
.baseUrl("https://api.rairmmd.com/")
.addConverterFactory(GsonConverterFactory.create()) // 自動(dòng)把JSON變成對(duì)象
.client(OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor()).build())
.build()
// 3. 開始點(diǎn)菜!
val service = retrofit.create(NewsApiService::class.java)
val newsList = service.getLatestNews().body() // 直接拿到新聞列表對(duì)象
適合場(chǎng)景:快速對(duì)接標(biāo)準(zhǔn)REST API
Ktor簡(jiǎn)單配置
// 1. 創(chuàng)建客戶端(搭積木式配置)
val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true// JSON多了字段也不報(bào)錯(cuò)
})
}
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.HEADERS // 想看請(qǐng)求頭就調(diào)這個(gè)
}
}
// 2. 發(fā)起請(qǐng)求(像發(fā)微信一樣簡(jiǎn)單)
suspendfun fetchNews(): List<News> {
return httpClient.get("https://api.rairmmd.com/news/latest").body()
}
// 3. 高級(jí)玩法:動(dòng)態(tài)URL參數(shù)
val searchResults = httpClient.get {
url("https://api.rairmmd.com/search")
parameter("q", "熱門") // 自動(dòng)拼接到URL
parameter("page", 2)
}.body<List<Article>>()
亮點(diǎn)功能:想加什么功能就像裝插件,比如給請(qǐng)求自動(dòng)加Token:
HttpClient {
install(Auth) {
bearer {
loadTokens {
BearerTokens("token", "1122334455667788")
}
}
}
}
實(shí)例對(duì)比
案例1:新聞列表接口(Retrofit版)
// 接口定義
@GET("newsList")
suspendfun getNewsListByCategory(
@Query("category") category: String,
@Query("page") page: Int
): Response<NewsResponse>
// 使用方式
viewModelScope.launch {
try {
val response = newsService.getNewsListByCategory("熱門", 1)
if (response.isSuccessful) {
_newsList.value = response.body()?.data ?: emptyList()
}
} catch (e: Exception) {
// 處理異常
}
}
案例2:同一個(gè)功能(Ktor版)
suspend fun loadNewsList(category: String, page: Int) {
val result = httpClient.get {
url("https://api.rairmmd.com/newsList")
parameters {
append("category", category)
append("page", page.toString())
}
}
if (result.status == HttpStatusCode.OK) {
val newsData = result.body<NewsResponse>()
_newsList.value = newsData.data
}
}
選擇困難癥急救包
當(dāng)你需要...
- 快速上線 → Retrofit(老司機(jī)踩油門)
// 三行代碼搞定基礎(chǔ)配置
Retrofit.Builder()
.baseUrl("https://api.rairmmd.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
- 魔改請(qǐng)求 → Ktor(樂(lè)高式組裝)
// 自定義超時(shí)時(shí)間+重試機(jī)制
HttpClient(CIO) {
install(HttpTimeout) {
requestTimeoutMillis = 15000
}
install(HttpRequestRetry) {
maxRetries = 3
retryOnExceptionIf { _, cause ->
cause is ConnectTimeoutException
}
}
}
- 跨平臺(tái)開發(fā) → Ktor(一份代碼走天下)
// 共享common模塊中的網(wǎng)絡(luò)請(qǐng)求代碼
expect fun createHttpClient(): HttpClient
// iOS版實(shí)現(xiàn)
actual fun createHttpClient() = HttpClient(CIO) { /* iOS配置 */ }
// Android版實(shí)現(xiàn)
actual fun createHttpClient() = HttpClient(Android) { /* Android配置 */ }
過(guò)來(lái)人的忠告
Retrofit踩坑記錄
// 坑1:忘記加轉(zhuǎn)換器會(huì)直接崩潰!
Retrofit.Builder()
.baseUrl("https://api.rairmmd.com/")
// 漏了.addConverterFactory()
.build()
// 坑2:路徑參數(shù)要用@Path標(biāo)注
@GET("user/{id}/profile") // ? 正確姿勢(shì)
suspend fun getUserProfile(@Path("id") userId: String)
Ktor避雷指南
// 雷區(qū)1:協(xié)程作用域沒(méi)管理好
// 錯(cuò)誤示范 ?
fun loadData() {
GlobalScope.launch {
httpClient.get(...) // 可能導(dǎo)致內(nèi)存泄漏
}
}
// 正確姿勢(shì) ?
viewModelScope.launch {
httpClient.get(...)
}
// 雷區(qū)2:響應(yīng)解析類型不匹配
val news = httpClient.get("...").body<News>() // News類字段要和JSON完全對(duì)應(yīng)
終極選擇器
需求 | Retrofit | Ktor |
要對(duì)接10個(gè)API且今晚就要上線 | ?? 穩(wěn)如老狗 | ?? 現(xiàn)學(xué)來(lái)不及 |
要做直播間的彈幕推送 | ?? 輪詢費(fèi)勁 | ?? 長(zhǎng)連接神器 |
老板說(shuō)要"技術(shù)前沿" | ?? 經(jīng)典款 | ?? 新潮之選 |
團(tuán)隊(duì)全是Java老手 | ???? 無(wú)縫銜接 | ?? 語(yǔ)法震驚 |
想做小程序+APP+網(wǎng)頁(yè)三端通吃 | ? 各玩各的 | ?? 跨平臺(tái)大法 |
小項(xiàng)目隨便選,大項(xiàng)目看團(tuán)隊(duì)。實(shí)在糾結(jié)?