Kotlin Coroutine(協程)簡介

Kotlin Coroutine(協程)系列:
1. Kotlin Coroutine(協程) 簡介
2. Kotlin Coroutine(協程) 基本知識編程

協程介紹

協程是可掛起計算的實例。promise

它在概念上相似於線程,在這個意義上,它須要一個代碼塊運行,並具備相似的生命週期,它能夠被建立和啓動,但它不綁定到任何特定的線程。網絡

它能夠在一個線程中掛起其執行,並在另外一個線程中恢復。並且,像future 或 promise那樣,它在完結時可能伴隨着某種結果(值或異常)異步

協程開發人員這樣描述協程:異步編程

協程就像很是輕量級的線程。線程是由系統調度的,線程切換或線程阻塞的開銷都比較大。而協程依賴於線程,可是協程掛起時不須要阻塞線程,幾乎是無代價的,協程是由開發者控制的。因此協程也像用戶態的線程,很是輕量級,一個線程中能夠建立任意個協程。post

如上面所說,協程是由開發者本身控制的,所以在使用協程時咱們必定要記住一點,咱們必須知道咱們使用的協程在什麼時候掛起,它又在什麼時候從新恢復執行,若是無法知道這兩點,那就意味着咱們沒法控制協程,這個時候要慎用協程。性能

爲何使用協程

使用協程能夠提升線程的利用率。

一般咱們在Android中發起一個網絡請求都會經歷以下幾步:spa

  1. 在主線程中建立一個請求任務,如:Retrofit.Call
  2. 爲這個任務分配一個子線程去執行請求任務,如:調用Retrofit.Call.enqueue(callback)方法
  3. 子線程發起請求後將會阻塞等待網絡請求的返回結果,拿到結果後會將數據轉換成咱們須要的實體對象
  4. 在主線程中執行回調接口,執行餘下的業務操做

上面的流程中爲請求任務分配子線程通常都會配合線程池去作,以防止不斷建立線程而產生系統開銷,但在線程真正執行過程當中常常會遇到因磁盤IO或者是網絡請求等操做而致使線程阻塞,而此時當前線程只能阻塞等待,沒法作任何事情,在等待的這段時間裏線程至關於白白了浪費了自身資源,致使線程自身利用率低下。線程

Android中改用協程發起網絡請求流程以下:code

  1. 在主線程中建立一個協程,在協程中建立網絡請求任務
  2. 爲協程分配一個子線程去發起網絡請求
  3. 掛起子線程中的協程,此時僅僅是協程掛起,該子線程並無掛起阻塞
  4. 協程等待請求結果回來以後,會在子線程中從新恢復協程執行
  5. 在主線程中執行某個回調,拿到請求數據執行餘下的業務操做

在上述流程步驟3中掛起協程後子線程並不會阻塞,此時該子線程能夠被系統分配去作其餘的事情,當協程掛起結束時從新在子線程中恢復執行。這樣該線程就不會存在因阻塞致使的空閒浪費,提升了線程利用率。

總的來講,使用協程能夠最大程度的複用線程,經過讓線程滿載運行,從而達到充分的利用CPU提升系統性能。

告別回調地獄

使用協程另一個好處就是可讓開發者們告別異步編程中的回調地獄,簡化異步編程,讓寫異步代碼和寫同步代碼同樣簡單,加強了代碼的可讀性、可理解性和可維護性。

舉個例子
假定有個登陸有以下流程:

  1. 發請求獲取用戶token
  2. 根據token獲取用戶信息

經常使用實現方式代碼以下:

fun login() {
    requestToken { token ->
        requestUserInfo(token) { user ->
            Log.i("tag", user.toString())
        }
    }
}
複製代碼

上面的例子是Android開發中常常會遇到的問題,一個請求依賴前一個請求的結果,這個時候常常會出現這樣的寫法,在第一個請求的成功回調中根據請求結果發起第二個網絡請求。這裏還只存在兩層的嵌套,試想一下,若是嵌套層次出現4次,5次,甚至更多會出現怎樣的狀況,估計開發者本身寫起來都會崩潰。

使用RxJava實現代碼以下:

Single.fromCallable { requestToken() }
    .map { token -> requestUserInfo(tokenm) }
    .subscribe(
        { user -> Log.i("tag", user.toString()) }, // onSuccess
        { e -> e.printStackTrace() }  // onError
    )
複製代碼

使用RxJava的實現方式雖然將回調嵌套改爲了鏈式寫法,閱讀起來要稍微好點,可是依然存在回調並且增長了實現的複雜度,對不熟悉RxJava的人來講更少增長了難度。

使用協程實現方式代碼以下:

fun login() {
    val token = requestToken()
    val user = requestUserInfo(token)
    Log.i("tag", user.toString())
}
複製代碼

怎麼樣,使用協程的寫法是否是簡便不少,並且看起來很是符合人們的閱讀和理解習慣。

相關文章
相關標籤/搜索