參考文章:
https://kaixue.io/kotlin-coroutines-1/
https://johnnyshieh.me/posts/kotlin-coroutine-introduction/
在kotlin中協程的概念
kotlin協程這個概念不是一天兩天提出的了,有興趣想學一學,但是在網上你可能會看到很多非常專業的術語:
協程和線程類似;就像一種輕量級的線程;是協作式的,不需要線程的同步操作;協程是使用者态的,他的切換不需要和作業系統互動............
等等一些話。
講道理,看完這些話仿佛看到了一個超越自己認知的新東西,這些話在其他的語言中可能是對的,但是和kotlin中的協程概念不同:
kotlin協程是一個官方提供的線程排程的api,使用kotlin協程,可以用看似同步的方式,寫出異步的代碼。
kotlin常用api
CoroutineScope:協程對象本身,可直接通過CoroutineScope構造方法new對象,或使用GlobalScope全局協程對象,或繼承自定義CoroutineScope(庫中提供了CoroutineScope的實作類,在之後有介紹)
CoroutineContext:協程上下文,主要由協程的Job和CoroutineDispatcher組成;CoroutineContext作為CoroutineScope的參數使用。
CoroutineDispatcher:協程排程器,通過該變量可指定協程所運作的線程。
Dispatchers.Main //指定任務運作在主線程
Dispatchers.IO //指定任務運作在子線程,例如網絡請求,檔案讀取
Dispatchers.Default //未指定Dispatchers時,有JVM的共享線程池支援
建立協程常用的方法主要有launch和async(runBlocking官方注釋中說道該方法會阻塞目前線程,直到使用runBlocking啟動的協程運作結束才會恢複目前線程,一般用于main方法和test測試中,之後就不提他的使用了)
使用launch手動建立一個協程:
CoroutineScope(Dispatchers.Main).launch { //new CoroutineScope對象,并建立協程
}
GlobalScope.launch { //使用GlobalScop建立協程
}
MainScope().launch { //CoroutineScope子類MainScope, 預設運作在主線程中,建立協程
}
使用async建立一個協程:
val deferred = MainScope().async {
}
使用async方法建立協程會傳回一個deferred對象,通過deferred.await()挂起函數,可以得到異步擷取的值。
Retrofit+RxJava與Retrofit+coroutine對比:
下面通過一個簡單的例子對将coroutine和rxjava作對比:
使用retrofit+rxjava:
interface ApiService {
@GET("/users.json")
fun getUsers(): Observable<MainUserResult>
}
class MainAct : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun getUsersData() {
apiService.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe ({
})
}
}
retrofit+coroutine:
@GET("/users.json")
fun getUsers(): Call<MainUserResult>
///
class MainAct : AppCompatActivity(),
CoroutineScope by MainScope() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun getUsersData() {
launch(Dispatchers.Main) {
val result = apiService.getUsers().await()
}
}
}
我使用kotlin委托的方式, 讓MainActivity實作CoroutineScope接口,并委托給MainScope,這樣就可以在MainActivity中直接使用launch,而不需要每次都建構CoroutineScope對象了。而retrofit的Call對象,對coronutine有支援,提供了挂起方法await:
suspend fun <T : Any> Call<T>.await(): T {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
val body = response.body()
if (body == null) {
//.....
} else {
continuation.resume(body)
}
} else {
continuation.resumeWithException(HttpException(response))
}
}
override fun onFailure(call: Call<T>, t: Throwable) {
continuation.resumeWithException(t)
}
})
}
}
有時候可能我們需要兩個api同時請求,同時擷取結果,在RxJava中我們使用zip表達式實作該功能,在retrofit + coroutine中可以直接調用await方法,就可以實作:
launch(Dispatchers.Main) {
val userResult = apiService.getUsers().await()
val user = apiService.getUser("1111").await()
handleData(userResult, user)
}
下一篇會對kotlin coroutine異常處理,以及coroutine部分源碼進行分析。