Kotlin-協程是什麼?

Hi你好,新同窗。很高興,你終於追尋這個問題了,也許你正感到迷茫,各路大神對協程的理解不一,有人說它是線程框架,有人說它比線程更輕,但願我這篇博文能夠幫你從另外一個角度簡單理解協程。html

請相信一句話,任何解釋從第二我的口中說出時,可能已經存在了變化。而官網是咱們接觸任何技術最必要的門檻。因此請打開Kotlin中文網。不少人說kotlin官網教程很不詳細,其實否則,kotlin中文網教程很詳細。回到正題:java

什麼是協程?

image-20200115165921441

  • 異步編程
  • 體驗
  • 語言級
  • 理念

注意上面幾個關鍵點和一些實際使用,不難明白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 函數時,線程並無停下,相對來講,只是咱們的協程代碼塊被掛起,等待恢復。只有前面的掛起函數執行結束,咱們的協程代碼塊才能繼續執行。借用一幅圖來講明以下:框架

img

因此所謂的掛起實際上是代碼層次的一個處理,從而使得咱們能夠以同步形式去寫異步的代碼。異步

非阻塞程序?

所謂的非阻塞,其實就是切換了線程,觀察打印日誌變化,咱們能夠發現,當咱們直接 GlobalScope.launch 啓動一個協程時,此時運行的線程爲默認的線程,因此協程被稱爲非阻塞的實現方式。async


suspend關鍵字的做用

先看下面的圖ide

image-20200115191719199

當使用suspend修飾後的函數咱們稱其爲掛起函數,那麼掛起函數有什麼做用?爲何test 的suspend 標誌是黑的?

繼續看截圖

image-20200115192201091

掛起函數爲何只能在掛起函數中使用呢??

咱們再繼續往下看:看一下java字節碼

image-20200115193946330

這個 Continuation是什麼呢?按照字面意思,意思爲延續。那咱們該怎麼理解呢?

通常來講 Continuation 表明的是<剩餘的計算的概念>,也就是 接下來要執行的代碼。官方的解釋叫作 掛起點

好比我有一段代碼:

println("123".length)
複製代碼

最早執行的是"123".length,而後執行 println打印長度,此時執行 "123".length 就能夠當作一個掛起點,也就是代碼從這裏中止,等待計算出結果,然而此時內部線程卻沒有中止,當計算完的時候,也就是掛起結束,此時接着執行咱們的打印語句。

切換到咱們的suspend中,它表明的就是一個***標誌*** 的做用,有suspend修飾的表明的函數叫作掛起函數,當編譯器碰到這個標誌的函數,就知道它是一個可能會耗時的操做。

掛起函數爲何只能在掛起函數中使用呢?

image-20200115200403816

由於普通函數參數並無帶 Continuation啊,至關於沒有掛起點,編譯器沒法判斷,因此此時會報錯。

爲何test 的suspend 標誌是黑的?

編譯器知道它是掛起函數,可是test內部沒有掛起函數啊,因此此時編譯器提示。

那這個函數就一點做用沒有?

有做用,就是限制這個函數只能掛起函數調用。


在Android中使用

倒計時功能

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(){
    //記得關閉
  }
  
}
複製代碼

Jietu20200115-203101-HD


結合 ViewModel 使用

class MainViewModel : ViewModel() {
    fun test() {
        viewModelScope.launch {

        }
    }
}
複製代碼

在ViewModel取消時,協程將自動關閉


結合 Lifecycle 使用

導入如下依賴

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc03"
複製代碼

因而剛纔的倒計時代碼改成以下:

class Main3Activity : AppCompatActivity() {
  			....
        lifecycleScope.launch {
           ...
        }
    }
}
複製代碼

一樣,當fragment或者Activity關閉時,協程一樣將自動關閉。

查看源碼,會發現,viewModel中的 viewModelScope 和 Lifecycle lifecycleScope,實現方式一模一樣:

image-20200115204534210

本篇,咱們沒有過多的從源碼上去追尋,協程究竟是什麼,儘可能從語法,使用者的角度入手,帶着你們從側面去理解協程,但願你們都能有本身的理解。

若是但願深刻研究,推薦如下博客:

Beenyhuo

相關文章
相關標籤/搜索