參考文章:
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部分源碼進行分析。