協程,全稱能夠譯做協同程序,不少語言都有這個概念和具體實現,以前入門Python的時候接觸過,而Kotlin其實也早就有這個擴展功能庫了,只不過以前一直處於實驗階段,不過前段時間1.0的正式版終於出了,網上的相關博客也多了起來,通過這幾天的學習我也來作下小結吧。android
首先貼下Kotlin協程的官方github地址kotlinx.coroutines,下面的配置都是參照這裏的說明,並且裏面還貼心的給咱們準備了不少基礎的示例代碼,感興趣的的小夥伴稍後能夠去看看。git
首先配置下Kotlin版本github
buildscript {
ext.kotlin_version = '1.3.11'
}
複製代碼
而後引入依賴,目前最新版是1.1.0bash
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存活,協程可正常執行完
}
複製代碼
運行結果:ide
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版本中,還有ATOMIC
和UNDISPATCHED
兩個額外的模式,可是如今仍是實驗版,這裏很少介紹。結果如上。CoroutineScope.launch
一共有三個參數,而後介紹其餘兩個:學習
context: CoroutineContext = EmptyCoroutineContext
:協程上下文block: suspend CoroutineScope.() -> Unit
:閉包參數,定義協程內須要執行的操做。返回值爲Job對象。gradle
經過上面的例子,咱們知道了一個重要的點launch
函數是有返回值的,它是一個Job
的接口類型,除了配合LAZY
來本身啓動一個協程,下面介紹下其餘幾個重要方法:ui
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
的擴展函數,它結合了 cancel
和 join
的調用,來看下它的實現: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項目的配置,協程的一些基本操做,掛起函數的概念,你們對協程有一個基本的概念,下一篇將講下協程的更多知識。