忘記Rxjava吧,你應該試試Kotlin的協程

滑板上的老砒霜發表於2018-12-19

0.前言

協程以前一直是Kotlin作為實驗性的一個庫,前些日子發現1.3版本的kotlin relese了協程,所以就找時間研究了一下,本來早就想寫這篇文章了,但是因為離職換工作的原因,遲遲未能動筆,這兩天終於算搞完了,記錄一下我對協程的一些理解。

1.什麼是協程

1.1協程定義

我第一次接觸協程是在python的教程裡,當時廖雪峰在其中的解釋還是不錯的,這裡拿來用來解釋一下:子程式,或者稱為函式,在所有語言中都是層級呼叫,比如A呼叫B,B在執行過程中又呼叫了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。所以子程式呼叫是通過棧實現的,一個執行緒就是執行一個子程式。子程式呼叫總是一個入口,一次返回,呼叫順序是明確的。而協程的呼叫和子程式不同。協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。注意,在一個子程式中中斷,去執行其他子程式,不是函式呼叫,有點類似CPU的中斷。

忘記Rxjava吧,你應該試試Kotlin的協程

看這個圖,協程就是這樣,在一個執行緒中順序執行的,先執行一段程式,這裡用continuation表示,然後遇到suspension point是,程式懸掛,進行下一個continuation子程式的執行。

1.2協程和執行緒的關係

協程和執行緒,都能用來實現非同步呼叫,但是這兩者之間是有本質區別的

(1)協程是編譯器級別的,執行緒是系統級別的。協程的切換是由程式來控制的,執行緒的切換是由作業系統來控制的。

(2)協程是協作式的,執行緒是搶佔式的。協程是由程式來控制什麼時候進行切換的,而執行緒是有作業系統來決定執行緒之間的切換的。

(3)一個執行緒可以包含多個協程。

(4)Java中,多執行緒可以充分利用多核cpu,協程是在一個執行緒中執行。

(5)協程適合io密集型的程式,多執行緒適合計算密集型的程式(適用於多核cpu的情況)。當你的程式大部分是檔案讀寫操作或者網路請求操作的時候,這時你應該首選協程而不是多執行緒,首先這些操作大部分不是利用cpu進行計算而是等待資料的讀寫,其次因為協程執行效率較高,子程式切換不是執行緒切換,是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。 (6)使用協程可以順序呼叫非同步程式碼,避免回撥地獄。

2.簡單用法

這裡我打算模仿一個網路請求,點選button傳送網路請求,顯示一個progressbar打轉,返回結果後一個textview顯示結果並隱藏progressbar 先看一下佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <TextView
            android:id="@+id/timeTV"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <Button
            android:id="@+id/sendBT"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SEND"
            android:layout_gravity="center"/>
    <ProgressBar
            android:layout_gravity="center"
            android:visibility="gone"
            android:id="@+id/loadingPB"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

</FrameLayout>
複製程式碼

一個Button,一個TextView,一個ProgressBar

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        sendBT.setOnClickListener {

            coroutineSend()

        }
    }

    private fun coroutineSend() {
        val uiScope = CoroutineScope(Dispatchers.Main)
        uiScope.launch {
            loadingPB.visibility = View.VISIBLE
            val deffer = async(Dispatchers.Default) {
                getCoroutineResult()
            }
            val coroutineResult = deffer.await()
            timeTV.text = "get $coroutineResult"
            loadingPB.visibility = View.GONE
        }

    }

    private suspend fun getCoroutineResult(): String {

        delay(9000L)
        return "coroutine result"
    }
}

複製程式碼

首先建立了一個CoroutineScope,所有協程都執行在CoroutineScope中,建立CoroutineScop中傳入引數Dispatchers.Main,這是一個協程排程器,它確定了相應的協程在執行時使用一個或多個執行緒。協程排程器可以將協程的執行侷限在指定的執行緒中,排程它執行線上程池中或讓它不受限的執行。 呼叫launch,就啟動了一個協程,launch方法會返回一個job,呼叫cancel方法可以取消這個協程的進行。可以看到在協程裡我們先展示出loadingPB,然後呼叫async又啟動一個協程,同時使用Dispatchers.Default這個協程排程器,它將使協程在執行時使用一個DefaultDispatcher-worker-1執行緒,這裡為什麼使用async而沒有使用launch,是因為async會返回一個Deferred物件,呼叫其await方法可以阻塞執行流等到協程執行完畢返回結果,這樣可以得到一個返回值,在這個async建立的協程裡使用了使用了suspend方法

 private suspend fun getCoroutineResult(): String {

        delay(9000L)
        return "coroutine result"
    }
複製程式碼

先休眠9秒鐘,然後返回一個字串,注意這裡這個delay也是suspend方法,一個suspend方法只能在協程或者suspend方法裡呼叫。關於協程還有一些其他的建立和使用方法,有興趣的可以去看看官方教程。

3.Rxjava VS 協程

協程相對RxJava有什麼優點呢?

(1)RxJava堆疊可讀性查,一旦出現問題,堆疊資訊爆炸,難以定位問題,而協程就可以避免這個問題

(2)協程用同步的方式寫非同步的程式碼,美好了生活,方便程式碼閱讀。

(3)協程學習曲線比較平坦,相對於RxJava,協程對初學者更易於學習。

4.最後

這年頭用Kotlin來開發android應用確實越來越爽快了,一些新的特性也逐漸加入到Kotlin中,值得更加學習,當然還有Flutter,以後會陸續寫幾個關於Flutter的文章,畢竟release了,我對它是十分看好的。

忘記Rxjava吧,你應該試試Kotlin的協程

關注我的公眾號

相關文章