Kotlin 在非同步處理上有新的方法,協程 Coroutine。Coroutine 不會像 Thread 會耗費大量的資源,能在原本的線程上創建極為輕量的協程,且較不會發生記憶體洩漏的情況。
導入 Coroutine
在 build.gradle 中添加依賴項
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2'
}若要在 Android 中使用需要再添加 Android 依賴
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'第一個 Coroutine
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { //在後台啟動一個新的縣協程
delay(1000L) //非阻塞式的等待 1 秒鐘
println("World!")
}
println("Hello,")
Thread.sleep(2000L) // 阻塞主線程兩秒確保主線程存活
}上面的程式碼輸出結果:
Hellow,
World基本上 Coroutine 就是輕量的協程
也可以分別將 GlobalScope.launch{...} 和 delay(...) 替換成thread { ... } 和 Thread.Sleap(...),也可以得到相同的結果,可以嘗試一下。
如果只將 GlobalScope.launch{...} 替換成 thread{...} 你會得到以下錯誤:
Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function因為 delay() 是一個特殊的 suspend function (有人譯作 掛起函數),他不會阻塞線程,但是會 suspend 協程,而且只能在協程中使用。
橋接阻塞和非阻塞的世界
上面的範例中同時使用了非阻塞式的 delay() 和阻塞式的 Thread.sleap(),這樣很容易讓我們混淆哪個會阻塞線程。下面我們使用 runblocking{...} 來阻塞線程
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在後台啟動一個新的協程
delay(1000L)
println("World!")
}
println("Hello,")
runBlocking { // 這個表達式會阻塞主線程
delay(2000L) // 延遲兩秒來確保主線程存活
}
}結果基本上是相似的,只是都是使用了非組塞式的 delay()。調用了 runblocking{...} 的主線程會被阻塞直到 runblocking{...} 內的協程執行完畢。
下面用一個更合乎慣用法的方法在寫一次,用 runblocking{...} 來包裝 main 方法:
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> { // 開始執行主協程
GlobalScope.launch { // 在後台啓動一個協程並繼續執行
delay(1000L)
println("World!")
}
println("Hello,")
delay(2000L) // 延遲 2 秒來確保主線程存活
}這裡的 runBlocking {...} 用來啟動主線程。我們顯式指定了其返回類型 Unit,因為在 Kotlin 中 main 方法必須回傳 Unit。
等待一個作業完成
延遲一段時間來確保協程的運行並不是一個好辦法利用 job.join() 來確保工作執行結束。
val job: Job = GlobalScope.launch {
delay(1000L)
println("World!")
}
println("Hello,")
job.join()launch 會回傳一個 Job 物件,而 job.join() 其實就是會等待 job 的工作完成再繼續持行。
我們也可以利用 job.cancel() 取消協程:
val job: Job = GlobalScope.launch {
delay(1000L)
println("World!")
}
println("Hello,")
job.cancel()但是如果 job 已經完成工作,cancel 是不會發生任何事。