前言
使用kotlin協程也有一段時間了,給我最大的感受就是完全可以替代Rxjava了,並且寫起來更加的簡潔。
6月份Retrofit發佈的2.6.0版本內部支持了kotlin協程中的掛起(suspend)修飾符,這就意味着我們可以更加方便的用Retrofit結合kotlin協程來實現網絡請求了。
之前我都是使用Rxjava2+Retrofit實現網絡請求的功能,然後加入了AutoDispose來實現自動解綁以免發生內存泄漏的問題,感興趣的可以看看AutoDispose代替RxLifecycle優雅的解決RxJava內存泄漏問題
那協程有沒有自動解綁的東西呢。當然有了
目前google官方也給我們提供了androidx.lifecycle:lifecycle-viewmodel-ktx
的依賴包,給ViewModel中擴展了一個作用域叫viewModelScope
viewModelScope是一個綁定到當前viewModel的作用域 當ViewModel被清除時會自動取消該作用域,所以不用擔心內存泄漏爲問題
那這樣一來,我們完全可以使用Retrofit+Coroutines這個方案來代替之前用Retrofit+Rxjava+AutoDispose的方案了。
話不多說,直接上代碼
我在網上找了個公開的API接口 https://www.apiopen.top/novelApi
我們就請求這個接口了
添加依賴
首先是添加需要的依賴包,如下
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0"
implementation "com.squareup.okhttp3:okhttp:4.2.0"
implementation "com.squareup.retrofit2:retrofit:2.6.1"
implementation "com.squareup.okhttp3:logging-interceptor:4.2.0"
implementation "com.squareup.retrofit2:converter-gson:2.6.1"
代碼
在看下面的代碼之前,你可能需要先看一下下面幾篇文章,如果你都知道的話 直接跳過即可
對ViewModel和LiveData 還不是很瞭解的話先看一下這篇文章
mvp過渡到mvvm(Android 架構組件)
關於一些好用的擴展方法,看一下這個
Kotlin基於RxJava的擴展方法(超級好用)
這裏我們主要是看對數據類的擴展,節省一些無用代碼
關於Retrofit的工廠類這裏我就不放出來了,估計大家的代碼都差不多,需要的可以看demo,這裏我就不再浪費篇幅,直接用了。
實體類
響應基類
data class BaseResp<T>(
var code: Int = 0,
var msg: String = "",
var `data`: T
)
小說數據實體類
package com.yzq.coroutineretofitmvvm.bean
import com.google.gson.annotations.SerializedName
data class Fiction(
var bid: String = "",
var bookname: String = "",
var introduction: String = "",
@SerializedName("book_info")
var bookInfo: String = "",
var chapterid: String = "",
var topic: String = "",
@SerializedName("topic_first")
var topicFirst: String = "",
@SerializedName("date_updated")
var dateUpdated: Int = 0,
var author: String = "",
@SerializedName("author_name")
var authorName: String = "",
@SerializedName("top_class")
var topClass: String = "",
var state: String = "",
var readCount: String = "",
var praiseCount: String = "",
@SerializedName("stat_name")
var statName: String = "",
@SerializedName("class_name")
var className: String = "",
var size: String = "",
@SerializedName("book_cover")
var bookCover: String = "",
@SerializedName("chapterid_first")
var chapteridFirst: String = "",
var chargeMode: String = "",
var digest: String = "",
var price: String = "",
var tag: List<String> = listOf(),
@SerializedName("is_new")
var isNew: Int = 0,
var discountNum: Int = 0,
@SerializedName("quick_price")
var quickPrice: Int = 0,
var formats: String = "",
@SerializedName("audiobook_playCount")
var audiobookPlayCount: String = "",
var chapterNum: String = "",
var isShortStory: Boolean = false,
var userid: String = "",
@SerializedName("search_heat")
var searchHeat: String = "",
@SerializedName("num_click")
var numClick: String = "",
@SerializedName("recommend_num")
var recommendNum: String = "",
@SerializedName("first_cate_id")
var firstCateId: String = "",
@SerializedName("first_cate_name")
var firstCateName: String = "",
var reason: String = ""
)
定義請求接口
這個沒什麼好說的,需要注意的就是我們的方法前面用 suspend 修飾
interface ApiService {
@GET("https://www.apiopen.top/novelApi")
suspend fun getFictions(): BaseResp<List<Fiction>>
}
用於解析響應數據的擴展方法
/*數據解析擴展函數*/
fun <T> BaseResp<T>.dataConvert(): T {
if (code == 200) {
return data
} else {
throw Exception(msg)
}
}
ViewModel
註釋也很詳細了,這裏就不多說了
class NetViewModel : ViewModel() {
var fictions = MutableLiveData<List<Fiction>>()
fun getFictions() {
/*viewModelScope是一個綁定到當前viewModel的作用域 當ViewModel被清除時會自動取消該作用域,所以不用擔心內存泄漏爲問題*/
viewModelScope.launch {
try {
/*withContext表示掛起塊 配合Retrofit聲明的suspend函數執行 該塊會掛起直到裏面的網絡請求完成 最一行就是返回值*/
val data = withContext(Dispatchers.IO) {
/*dataConvert擴展函數可以很方便的解析出我們想要的數據 接口很多的情況下下可以節省不少無用代碼*/
RetrofitFactory.instance.getService(ApiService::class.java)
.getFictions().dataConvert()
}
/*給LiveData賦值 ui會自動更新*/
fictions.value = data
} catch (e: Exception) {
/*請求異常的話在這裏處理*/
e.printStackTrace()
Log.i("請求失敗", "${e.message}")
}
}
}
}
Activity
Activity中代碼就很簡單了,主要就是創建ViewModel示例,更新ui
class MainActivity : AppCompatActivity() {
private lateinit var netViewModel: NetViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/*創建viewmodel*/
netViewModel = ViewModelProviders.of(this).get(NetViewModel::class.java)
btn.setOnClickListener {
/*請求數據*/
netViewModel.getFictions()
}
/*數據發生變化時更新ui*/
netViewModel.fictions.observe(this, Observer {
tv.text = Gson().toJson(it)
})
}
}
下面我們來看看運行結果:
可以看到,網絡請求的功能已經實現了。
那對比RxJava的實現方式,你覺得哪種方式更好呢?
好了,本篇文章到此結束。
如果你覺得本文對你有幫助,麻煩動動手指頂一下,算是對本文的一個認可,如果文中有什麼錯誤的地方,還望指正,轉載請註明轉自喻志強的博客 ,謝謝!