協程之前一直是Kotlin做爲實驗性的一個庫,前些日子發現1.3版本的kotlin relese了協程,因此就找時間研究了一下,原本早就想寫這篇文章了,可是由於離職換工做的緣由,遲遲未能動筆,這兩天終於算搞完了,記錄一下我對協程的一些理解。java
我第一次接觸協程是在python的教程裏,當時廖雪峯在其中的解釋仍是不錯的,這裏拿來用來解釋一下:子程序,或者稱爲函數,在全部語言中都是層級調用,好比A調用B,B在執行過程當中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。因此子程序調用是經過棧實現的,一個線程就是執行一個子程序。子程序調用老是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不一樣。協程看上去也是子程序,但執行過程當中,在子程序內部可中斷,而後轉而執行別的子程序,在適當的時候再返回來接着執行。注意,在一個子程序中中斷,去執行其餘子程序,不是函數調用,有點相似CPU的中斷。python
看這個圖,協程就是這樣,在一個線程中順序執行的,先執行一段程序,這裏用continuation表示,而後遇到suspension point是,程序懸掛,進行下一個continuation子程序的運行。android
協程和線程,都能用來實現異步調用,可是這二者之間是有本質區別的bash
(1)協程是編譯器級別的,線程是系統級別的。協程的切換是由程序來控制的,線程的切換是由操做系統來控制的。網絡
(2)協程是協做式的,線程是搶佔式的。協程是由程序來控制何時進行切換的,而線程是有操做系統來決定線程之間的切換的。多線程
(3)一個線程能夠包含多個協程。異步
(4)Java中,多線程能夠充分利用多核cpu,協程是在一個線程中執行。async
(5)協程適合io密集型的程序,多線程適合計算密集型的程序(適用於多核cpu的狀況)。當你的程序大部分是文件讀寫操做或者網絡請求操做的時候,這時你應該首選協程而不是多線程,首先這些操做大部分不是利用cpu進行計算而是等待數據的讀寫,其次由於協程執行效率較高,子程序切換不是線程切換,是由程序自身控制,所以,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優點就越明顯。 (6)使用協程能夠順序調用異步代碼,避免回調地獄。ide
這裏我打算模仿一個網絡請求,點擊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方法裏調用。關於協程還有一些其餘的建立和使用方法,有興趣的能夠去看看官方教程。
協程相對RxJava有什麼優勢呢?
(1)RxJava堆棧可讀性查,一旦出現問題,堆棧信息爆炸,難以定位問題,而協程就能夠避免這個問題
(2)協程用同步的方式寫異步的代碼,美好了生活,方便代碼閱讀。
(3)協程學習曲線比較平坦,相對於RxJava,協程對初學者更易於學習。
這年頭用Kotlin來開發android應用確實愈來愈爽快了,一些新的特性也逐漸加入到Kotlin中,值得更加學習,固然還有Flutter,之後會陸續寫幾個關於Flutter的文章,畢竟release了,我對它是十分看好的。
關注個人公衆號