天天看點

kotlin協程一 :kotlin協程介紹以及android中簡單使用在kotlin中協程的概念kotlin常用apiRetrofit+RxJava與Retrofit+coroutine對比:

參考文章:

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部分源碼進行分析。