Kotlin協程快速入門

大頭呆發表於2018-12-25

協程,全稱可以譯作協同程式,很多語言都有這個概念和具體實現,之前入門Python的時候接觸過,而Kotlin其實也早就有這個擴充套件功能庫了,只不過之前一直處於實驗階段,不過前段時間1.0的正式版終於出了,網上的相關部落格也多了起來,經過這幾天的學習我也來做下小結吧。

環境配置

首先貼下Kotlin協程的官方github地址kotlinx.coroutines,下面的配置都是參照這裡的說明,而且裡面還貼心的給我們準備了很多基礎的示例程式碼,感興趣的的小夥伴稍後可以去看看。

首先配置下Kotlin版本

buildscript { 
ext.kotlin_version = '1.3.11'
}複製程式碼

然後引入依賴,目前最新版是1.1.0

   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0'   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0'複製程式碼

配置很簡單,接下來幹什麼呢。當然是寫個協程版的Hello World了!

import kotlinx.coroutines.*fun main() { 
GlobalScope.launch {
// 建立並啟動一個協程 delay(1000L) // 延遲(掛起)1000毫秒,注意這不會阻塞執行緒 println("World!") //延遲之後執行列印
} println("Hello,") // 協程延遲的時候不會影響主執行緒的執行 Thread.sleep(2000L) // 阻塞執行緒2s,保證JVM存活,協程可正常執行完
}複製程式碼

執行結果:

2018-12-23 17:35:16.998 15539-15539/com.renny.kotlin I/System.out: Hello,2018-12-23 17:35:18.005 15539-18893/com.renny.kotlin I/System.out: World!複製程式碼

基礎語法

啟動模式

上面的協程啟動模式是預設的DEAFAULT,也就是建立並立即啟動的,我們也可以設定啟動模式為LAZY,來自己安排是什麼時候需要啟動:

fun main() { 
val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
println("World!")
} println("Hello,") job.start() Thread.sleep(2000L)
}複製程式碼

在我所採用的Kotlin 1.3版本中,還有ATOMICUNDISPATCHED兩個額外的模式,但是現在還是實驗版,這裡不多介紹。結果如上。CoroutineScope.launch一共有三個引數,然後介紹其他兩個:

  • context: CoroutineContext = EmptyCoroutineContext:協程上下文
  • block: suspend CoroutineScope.() ->
    Unit
    :閉包引數,定義協程內需要執行的操作。

返回值為Job物件。

Job類

通過上面的例子,我們知道了一個重要的點launch函式是有返回值的,它是一個Job的介面型別,除了配合LAZY來自己啟動一個協程,下面介紹下其他幾個重要方法:

  • job.cancel()取消一個協程
    fun main() { 
val job = GlobalScope.launch {
delay(1000L) println("World!")
} job.cancel() println("Hello,")
}複製程式碼

協程被取消了,所以只列印了Hello,

  • join()等待協程執行完畢
fun main() = runBlocking { 
val job = GlobalScope.launch {
delay(1000L) println("World!") delay(1000L)
} println("Hello,") job.join() println("Good!")
}複製程式碼

作用很像Thread.join()函式,join()後面的程式碼會等到協程結束再執行,結果如下:

2018-12-24 21:19:41.153 23484-23484/com.renny.kotlin I/System.out: Hello,2018-12-24 21:19:42.148 23484-24172/com.renny.kotlin I/System.out: World!2018-12-24 21:19:43.161 23484-23484/com.renny.kotlin I/System.out: Good!複製程式碼
  • job.cancelAndJoin()等待協程執行完畢然後再取消這是一個 Job 的擴充套件函式,它結合了 canceljoin的呼叫,來看下它的實現:
public suspend fun Job.cancelAndJoin() { 
cancel() return join()
}複製程式碼

掛起函式

細心的同學可能發現了兩個不通點,Job.join()函式被一個名字叫runBlocking的包圍了,而Job.start()Job.cancel都沒有;Job.cancelAndJoin()前面被一個特殊的關鍵詞suspend修飾了,這有什麼用呢?

其實通過檢視原始碼,Job.join()也被suspend修飾了,所以這是一個suspend(掛起)函式,掛起函式必須在協程中或者掛起函式中使用,因為呼叫了Job.join()Job.cancelAndJoin()也必須加上suspend宣告。事實上,要啟動協程,必須至少有一個掛起函式。

協程及協程掛起:

協程是通過編譯技術實現的,不需要虛擬機器VM/作業系統OS的支援,通過相關程式碼來生效協程的掛起幾乎無代價,無需上下文切換或涉及OS協程不能在隨機指令中掛起,只能在掛起點掛起(呼叫標記函式)!複製程式碼

子協程

上面我們都是線上程中開啟一個協程,同樣在協程中我們也能開啟另一個協程,所以我們再來看下複雜點的例子:

 fun main() = runBlocking { 
GlobalScope.launch {
delay(1000L) println("World!")
} println("Hello,") runBlocking {
delay(2000L)
}
}複製程式碼

最外層的runBlocking為最高階的協程 (一般為主協程), 其他協程如launch {
}
因為層級較低能跑在runBlocking裡。runBlocking的最大特點就是它的delay()可以阻塞當前的執行緒,和Thread.sleep()有著相同的效果。列印的日誌同第一個示例。

Job類中會儲存子協程的集合:

public val children: Sequence<
Job>
複製程式碼

同樣也提供了取消全部子協程的方法:

public fun Job.cancelChildren() { 
children.forEach {
it.cancel()
}
}複製程式碼

小結

這篇主要介紹了協程引入Android專案的配置,協程的一些基本操作,掛起函式的概念,大家對協程有一個基本的概念,下一篇將講下協程的更多知識。

參考文章

Kotlin協程官方文件

來源:https://juejin.im/post/5c1f44b25188254fb27660f1#comment

相關文章