Hi你好,新同窗。很高興,你終於追尋這個問題了,也許你正感到迷茫,各路大神對協程的理解不一,有人說它是線程框架,有人說它比線程更輕,但願我這篇博文能夠幫你從另外一個角度簡單理解協程。html
請相信一句話,任何解釋從第二我的口中說出時,可能已經存在了變化。而官網是咱們接觸任何技術最必要的門檻。因此請打開Kotlin中文網。不少人說kotlin官網教程很不詳細,其實否則,kotlin中文網教程很詳細。回到正題:java
注意上面幾個關鍵點和一些實際使用,不難明白android
Kotlin協程是基於Kotlin語法從而延伸的一個異步編程框架,它並無帶來多少性能上的體驗,它能實現的,你用線程池一樣也能夠實現,但對於使用角度的來講,協程努力打造一個 "同步方式,異步編程的" 思想,做爲開發者來講,咱們能夠更懶了,切換線程,withContext便可,協程帶來了開發上的溫馨,但這種溫馨是基於 Kotlin 的語法,並非別的。因此我但願你們關注協程時,多從語言角度去理解。編程
協程就是一個基於Kotlin語法的異步框架,它可使開發者以同步的方式,寫成異步的代碼,而無需關注多餘操做。就這麼簡單bash
suspend fun main() {
GlobalScope.launch {
println("啓動--${System.currentTimeMillis()}---${Thread.currentThread().name}")
delay(100)
println("掛起100ms--${System.currentTimeMillis()}---${Thread.currentThread().name}")
}
delay(1000)
}
複製代碼
啓動--1579085375166---DefaultDispatcher-worker-1
掛起100ms--1579085375275---DefaultDispatcher-worker-1
複製代碼
fun main() {
runBlocking(Dispatchers.IO) {
println("啓動--${System.currentTimeMillis()}")
val as1 = async {
delay(100)
println("併發1--${System.currentTimeMillis()}---${Thread.currentThread().name}")
"1"
}
val as2 = async {
delay(100)
println("併發2--${System.currentTimeMillis()}---${Thread.currentThread().name}")
"2"
}
println("as1=${as1.await()},as2=${as2.await()}")
}
}
複製代碼
啓動--1579085205199
併發1--1579085205313---DefaultDispatcher-worker-3
併發2--1579085205313---DefaultDispatcher-worker-2
as1=1,as2=2
複製代碼
觀察上面demo的運行結果,是否是很舒服,看起來同步的方式內部倒是在異步操做。那上面註釋中 掛起 是什麼意思呢?併發
觀察上面的打印日誌,咱們不難發現,在調用 delay 函數時,線程並無停下,相對來講,只是咱們的協程代碼塊被掛起,等待恢復。只有前面的掛起函數執行結束,咱們的協程代碼塊才能繼續執行。借用一幅圖來講明以下:框架
因此所謂的掛起實際上是代碼層次的一個處理,從而使得咱們能夠以同步形式去寫異步的代碼。異步
所謂的非阻塞,其實就是切換了線程,觀察打印日誌變化,咱們能夠發現,當咱們直接 GlobalScope.launch 啓動一個協程時,此時運行的線程爲默認的線程,因此協程被稱爲非阻塞的實現方式。async
先看下面的圖ide
當使用suspend修飾後的函數咱們稱其爲掛起函數,那麼掛起函數有什麼做用?爲何test 的suspend 標誌是黑的?
咱們再繼續往下看:看一下java字節碼
這個 Continuation是什麼呢?按照字面意思,意思爲延續。那咱們該怎麼理解呢?
通常來講 Continuation 表明的是<剩餘的計算的概念>,也就是 接下來要執行的代碼。官方的解釋叫作 掛起點
好比我有一段代碼:
println("123".length)
複製代碼
最早執行的是"123".length,而後執行 println打印長度,此時執行 "123".length 就能夠當作一個掛起點,也就是代碼從這裏中止,等待計算出結果,然而此時內部線程卻沒有中止,當計算完的時候,也就是掛起結束,此時接着執行咱們的打印語句。
切換到咱們的suspend中,它表明的就是一個***標誌*** 的做用,有suspend修飾的表明的函數叫作掛起函數,當編譯器碰到這個標誌的函數,就知道它是一個可能會耗時的操做。
由於普通函數參數並無帶 Continuation啊,至關於沒有掛起點,編譯器沒法判斷,因此此時會報錯。
編譯器知道它是掛起函數,可是test內部沒有掛起函數啊,因此此時編譯器提示。
那這個函數就一點做用沒有?
有做用,就是限制這個函數只能掛起函數調用。
class Main3Activity : AppCompatActivity() {
private val mainScope = CoroutineScope(Dispatchers.Default)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
progressBar.max = 100
mainScope.launch {
(1..100).forEach {
delay(100)
withContext(Dispatchers.Main) {
progressBar.secondaryProgress = it
}
}
}
}
onDestory(){
//記得關閉
}
}
複製代碼
class MainViewModel : ViewModel() {
fun test() {
viewModelScope.launch {
}
}
}
複製代碼
在ViewModel取消時,協程將自動關閉
導入如下依賴
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc03" 複製代碼
因而剛纔的倒計時代碼改成以下:
class Main3Activity : AppCompatActivity() {
....
lifecycleScope.launch {
...
}
}
}
複製代碼
一樣,當fragment或者Activity關閉時,協程一樣將自動關閉。
查看源碼,會發現,viewModel中的 viewModelScope 和 Lifecycle lifecycleScope,實現方式一模一樣:
本篇,咱們沒有過多的從源碼上去追尋,協程究竟是什麼,儘可能從語法,使用者的角度入手,帶着你們從側面去理解協程,但願你們都能有本身的理解。
若是但願深刻研究,推薦如下博客: