安卓 協(xié)程

協(xié)程: 協(xié)程最核心的點就是回窘,函數(shù)或者一段程序能夠被掛起诺擅,稍后再在掛起的位置恢復(fù)。掛起的函數(shù)要在協(xié)程里面執(zhí)行啡直。
特點: 1烁涌、更加輕量級,占用資源更少酒觅。
2撮执、避免“回調(diào)地獄”,增加代碼可讀性舷丹。
3抒钱、協(xié)程的掛起不阻塞線程。
1颜凯、協(xié)程的掛起與恢復(fù)
suspend- 也稱為掛起或暫停谋币,用于暫停執(zhí)行當前協(xié)程,并保存所有局部變量;
resume -用于讓已暫停的協(xié)程從其暫停處繼續(xù)執(zhí)行装获。
掛起函數(shù)只能在協(xié)程體內(nèi)或其他掛起函數(shù)內(nèi)調(diào)用

tv?.setOnClickListener {
            GlobalScope.launch(Dispatchers.Main) {
                //掛起瑞信,點擊N次響應(yīng)N次
                delay(4000)
                Log.i("jia", "-----")
            }
            //點擊一次無效因為阻塞了, 點擊N次也是響應(yīng)一次而且會崩潰
            Thread.sleep(4000)
            Log.i("jia", "=====")
        }

阻塞: 等98號技師穴豫,98號在忙凡简,中間不干其他的逼友,就一直等直到她結(jié)束
掛起: 等98號技師,98號在忙秤涩,前臺記下來帜乞,先看電影或者干其他的,結(jié)束后直接服務(wù)你
2筐眷、調(diào)度器
所有協(xié)程必須在調(diào)度器中運行黎烈,即使它們在主線程上運行也是如此。
Dispatchers.Main Android 上的主線程
用來處理UI交互和一些輕量級任務(wù):調(diào)用suspend函數(shù)匀谣、調(diào)用UI函教照棋、更新LiveData
Dispatchers.lO 非主線程
轉(zhuǎn)為磁盤和網(wǎng)絡(luò)IO進行優(yōu)化,用來處理數(shù)據(jù)庫武翎、文件烈炭、讀寫網(wǎng)絡(luò)處理
Dispatchers.Default 非主線程
專為CPU密集型任務(wù)進行了憂化:數(shù)組排序、JSON數(shù)據(jù)解析
3宝恶、協(xié)程作用域
定義協(xié)程必須指定其CoroutineScope符隙,它會跟蹤所有協(xié)程,同樣它還可以取消由它所啟動的所有協(xié)程
常用的相關(guān)API有:
GlobalScope垫毙,生命周期是process級別的霹疫,即使Activity或Fragment已經(jīng)被銷毀,協(xié)程仍然在執(zhí)行综芥。
MainScope丽蝎,在Activity中使用,可以在onDestroy()中取消協(xié)程毫痕。
viewModelScope征峦,只能在ViewModel中使用,綁定ViewModel的生命周期消请。
lifecycleScope,只能在Activity类腮、Fragment中使用臊泰,會綁定Activity和Fragment的生命周期。

4蚜枢、runBlocking缸逃、launch、async 操作符
join()厂抽、await()非阻塞式等待需频,直到線程結(jié)束,而不會將當前線程掛起
launch筷凤,返回一個Job并且不附帶任何結(jié)果值昭殉。多個launch依次執(zhí)行苞七,用join()等待。
async挪丢,返回一個Deferred蹂风,Deferred也是一個Job,可以使用.await()在一個延期的值上得到它的最終結(jié)果乾蓬。
多個async依次執(zhí)行,用await()等待。
4南窗、協(xié)程的啟動模式
DEFAULT:協(xié)程創(chuàng)建后牌里,立即開始調(diào)度,在調(diào)度前如果協(xié)程被取消死嗦,其將直接進入取消響應(yīng)的狀態(tài)趋距。
類似滴滴打車,立即開始調(diào)度不一定立馬有車越走,需要等待車來棚品,但是可以在車來之前取消。
ATOMIC:協(xié)程創(chuàng)建后廊敌,立即開始調(diào)度铜跑,協(xié)程執(zhí)行到第一個掛起點之前不響應(yīng)取消。
LAZY:只有協(xié)程被需要時骡澈,包括主動調(diào)用協(xié)程的start锅纺、join或者await等函數(shù)時才會開始調(diào)度,如果調(diào)度前就被取消肋殴,那么該協(xié)程將直接進入異常結(jié)束狀態(tài)囤锉。
UNDISPATCHED:協(xié)程創(chuàng)建后立即在當前函數(shù)調(diào)用棧中執(zhí)行,直到遇到第一個真正掛起的點护锤。

      //如何使用Dispatchers.IO調(diào)度器官地,執(zhí)行仍然在主線程就是用Coroutinestart.UNDISPATCHED
        val job =GlobalScope.async(context=Dispatchers.IO,start=Coroutinestart.UNDISPATCHED){
            println("thread:"+Thread.currentThread().name)
        }

5、協(xié)程的作用域構(gòu)建器 coroutineScope與runBlocking
runBlocking是常規(guī)函數(shù)烙懦,而coroutineScope是掛起函數(shù)驱入。
它們都會等待其協(xié)程體以及所有子協(xié)程結(jié)束,主要區(qū)別在于runBlocking方法會阻塞當前線程來等待氯析,而coroutineScope只是掛起亏较,會釋放底層線程用于其他用途。
coroutineScope與supervisorScope
coroutineScope:一個協(xié)程失敗了掩缓,所有其他兄弟協(xié)程也會被取消雪情。
supervisorScope:一個協(xié)程失敗了,不會影響其他兄弟協(xié)程你辣。

    suspend fun 將main() = runBlocking {
        coroutineScope { //這里換成supervisorScope巡通,子協(xié)程1就會執(zhí)行
            val job1 = launch {
                delay(5000)
                println("子協(xié)程1")
            }
            val job2 = launch {
                delay(1000)
                println("子協(xié)程2")
                throw Exception()   //子協(xié)程1不會執(zhí)行
            }
        }
    }

6尘执、Job對象
對于每一個創(chuàng)建的協(xié)程((通過launch或者async,會返回一個Job實例扁达,該實例是協(xié)程的唯一標示正卧,并且負責管理協(xié)程的生命周期。
一個任務(wù)可以包含一系列狀態(tài):新創(chuàng)建(New)跪解、活躍(Active)炉旷,完成中(Completing)、已完成(Completed),(取消中(Cancelling)和已取消(Cancelled)雖然我們無法直接訪問這些狀態(tài)叉讥,但是我們可以訪問Job的屬性: isActive窘行、isCancelled和iscompleted。
7图仓、CPU密集型任務(wù)取消
isActive是一個可以被使用在CoroutineScope中的擴展屬性罐盔,檢查Job是否處于活躍狀態(tài)。
ensureActive()救崔,如果job處于非活躍狀態(tài)惶看,這個方法會立即拋出異常。
yield函數(shù)會檢查所在協(xié)程的狀態(tài)六孵,如果已經(jīng)取消纬黎,則拋出CancellationException予以響應(yīng)。此外劫窒,它還會嘗試出讓線程的執(zhí)行權(quán)本今,給其他協(xié)程提供執(zhí)行機會。
處于取消中狀態(tài)的協(xié)程不能夠掛起(運行不能取消的代碼)主巍,當協(xié)程被取消后需要調(diào)用掛起函數(shù)冠息,我們需要將清理任務(wù)的代碼放置于 NonCancellable CoroutineContext 中

  fun yield() = runBlocking<Unit> {
        val startTime = System.currentTimeMillis()
        val job = launch(Dispatchers.Default) {
            var nextPrintTime = startTime
            var i = 0
            while (i < 5&& isActive) {//增加isActive狀態(tài) 來取消任務(wù)
                if (System.currentTimeMillis() >= nextPrintTime) {
//                    ensureActive()//下面這兩種方式都可以取消任務(wù)
//                    yield()
                    println("job:I'm sleeping ${i++}。孕索。逛艰。1")
                    nextPrintTime += 500
                }
            }
            try {
                delay(10000)
                println("job:I'm sleeping")
            }finally {
                println("job:I'm finally")
                withContext(NonCancellable){
                    //NonCancellable使用后,即使上述任務(wù)取消了搞旭,也不影響下面的執(zhí)行
                    delay(1300)
                    println("job:I'm back")
                }
            }
        }
        delay(1300)
        println("main:I'm tired of waiting!")
        job.cancelAndJoin()
        println("main:Now Ican guit.")
    }

8瓮孙、超時任務(wù)
很多情況下取消一個協(xié)程的理由是它有可能超時。
withTimeoutOrNull 通過返回null來進行超時操作选脊,從而替代拋出一個異常。
9脸甘、SupervisorJob
使用SupervisorJob時恳啥,一個子協(xié)程的運行失敗不會影響到其他子協(xié)程。Supervisorjob不會傳播異常給它的父級丹诀,它會讓子協(xié)程自己處理異常钝的。
這種需求常見于在作用域內(nèi)定義作業(yè)的UI組件翁垂,如果任何一個U的子作業(yè)執(zhí)行失敗了,它并不總是有必要取消整個UI組件硝桩,但是如果UI組件被銷毀了沿猜,由于它的結(jié)果不再被需要了,它就有必要使所有的子作業(yè)執(zhí)行失敗碗脊。
supervisorScope:當作業(yè)自身執(zhí)行失敗的時候啼肩,所有子作業(yè)都會被取消。
10衙伶、異常捕獲
使用CoroutineExceptionHandler對協(xié)程的異常進行捕獲祈坠。以下的條件被滿足時,異常就會被捕獲:矢劲。
時機:異常是被自動拋出異常的協(xié)程所拋出的(使用launch赦拘,而不是 async 時);
位置:在CoroutineScope的CoroutineContext中或在一個根協(xié)程(CoroutineScope 或者 supervisorScope 的直接子協(xié)程)中。
當子協(xié)程被取消時芬沉,不會取消它的父協(xié)程躺同,
如果一個協(xié)程遇到了CancellationException以外的異常,它將使用該異常取消它的父協(xié)程丸逸。當父協(xié)程的所有子協(xié)程都結(jié)束后蹋艺,異常才會被父協(xié)程處理。

 fun catchHandle() = runBlocking<Unit> {
        val handler= CoroutineExceptionHandler { coroutineContext, exception ->
           println("catch"+exception)
        }
        val job=GlobalScope.launch(handler) {
            throw AssertionError() //會被捕獲
            launch(handler) {
                throw AssertionError() //此內(nèi)部協(xié)程的方式不會被捕獲
            }
        }
        val job2=GlobalScope.async(handler)  {
            throw ArithmeticException()//不會被捕獲
        }
        job.join()
        job2.await()
    }

常見異常捕獲方式

val handler= CoroutineExceptionHandler { coroutineContext, exception ->
            println("catch"+exception)
        }
        tv?.setOnClickListener {
            GlobalScope.launch(handler) {
                Log.i("jia", "onclick")
                "abc".substring(10)//必閃退 ,但是加了異常捕獲椭员,就不會閃退了车海,還會打印閃退信息
            }
        }

11、Flow 與其他方式的區(qū)別
名為flow的Flow類型構(gòu)建器函數(shù)隘击。
flow{.. }構(gòu)建塊中的代碼可以掛起侍芝。
函數(shù)simpleFlow不再標有suspend修飾符。
流使用emit函數(shù)發(fā)射值埋同。
流使用collect函數(shù)收集值州叠。

//返回多個值,是異步的
fun simpleFlow() = flow<Int> {
    for (i in 1..3) {
        delay(1000)//假裝在一些重要的事情
        emit(i)//發(fā)射凶赁,產(chǎn)生一個元素
    }
}
fun collectFlow()= runBlocking<Unit> {
    simpleFlow().collect { value ->
        println(value)
    }
}

12咧栗、冷流
①、Flow是一種類似于序列的冷流虱肄,flow構(gòu)建器中的代碼直到流被收集的時候才運行致板。

 fun collectFlow()= runBlocking<Unit> {
        val flow =simpleFlow()
        printIn("Calling collect...")
        flow.collect{value ->printIn(value)}
        println("Calling collect again...")
        flow.collect{value ->printIn(value)}
    }

②、流的每次單獨收集都是按順序執(zhí)行的咏窿,除非使用特殊操作符.
從上游到下游每個過渡操作符都會處理每個發(fā)射出的值斟或,然后再交給末端操作符.
下列打印結(jié)果:
Collect string 2
Collect string 4

    fun collectFlow() = runBlocking<Unit> {
        //過濾偶數(shù)并將其映射到字符串
        (1..5).asFlow().filter {
            it % 2 == 0
        }.map {
            "string $it"
        }.collect {
            printIn("Collect $it")
        }
    }

③、flowOf構(gòu)建器定義了一個發(fā)射固定值集的流集嵌。
使用.asFlow()擴展函數(shù)萝挤,可以將各種集合與序列轉(zhuǎn)換為流御毅。

 fun collectFlow() = runBlocking<Unit> {
        flowOf("one", "two", "three")
            .onEach { delay(1000) }
            .collect { value -> printIn(value) }

        (1..3).asFlow().collect { value ->
            println(value)
        }
    }

④、流的收集總是在調(diào)用協(xié)程的上下文中發(fā)生怜珍,流的該屬性稱為上下文保存端蛆。
flow.{...}構(gòu)建器中的代碼必須遵循上下文保存屬性,并且不允許從其他上下文中發(fā)射(emit)酥泛。
flowOn操作符今豆,該函數(shù)用于更改流發(fā)射的上下文。

   fun simpleFlow() = flow<Int> {
        for (i in 1..3) {
            delay(1000)//假裝在一些重要的事情
            emit(i)//發(fā)射揭璃,產(chǎn)生一個元素
        }
    }.flowOn(Dispatchers.Default)

⑤晚凿、使用launchIn替換collect我們可以在單獨的協(xié)程中啟動流的收集。

//事件源
    fun events() = (1..3)
        .asFlow()
        .onEach { delay(1000) }
        .flowOn(Dispatchers.Default)
    fun collectFlow5() = runBlocking<Unit> {
        events().onEach { event -> println("Event: $event ${Thread.currentThread().name})") }
//            .collect()
            .launchIn(CoroutineScope(Dispatchers.IO))
    }

⑥瘦馍、為方便起見歼秽,流構(gòu)建器對每個發(fā)射值執(zhí)行附加的 ensureActive 檢測以進行取消的這意味著從 flow{..}發(fā)出的繁忙循環(huán)是可以取消的。
出于性能原因情组,大多數(shù)其他流操作不會自行執(zhí)行其他取消檢測燥筷,在協(xié)程處于繁忙循環(huán)的情況下,必須明確檢測是否取消院崇。
通過cancellable操作符來執(zhí)行此操作肆氓。
⑦、buffer()底瓣,并發(fā)運行流中發(fā)射元素的代碼谢揪。
conflate(),合并發(fā)射項捐凭,不對每個值進行處理拨扶。
collectLatest(),取消并重新發(fā)射最后一個值茁肠。
當必須更改CoroutineDispatcher時患民,flowOn操作符使用了相同的緩沖機制,但是buffer函數(shù)顯式地請求緩沖而不改變執(zhí)行上下文垦梆。

fun collectFlow6() = runBlocking<Unit> {
        simpleFlow()
            .buffer(50)//并發(fā)運行流中發(fā)射元素的代碼匹颤。
            .conflate()//合并發(fā)射項,不對每個值進行處理托猩。
            .collect { value ->
            delay(3000)
            println(value)
        }
    }

⑧印蓖、轉(zhuǎn)換轉(zhuǎn)換符

 suspend fun performRequest(request: Int): String {
        delay(1000)
        return "response $request"
    }

    fun simpleFlow() = flow<Int> {
        (1..3).asFlow()
            .map { performRequest(it) }
            .collect { value ->
                println(value)
            }
        (1..3).asFlow()
            .transform {
                emit("Making request $it")
                emit(performRequest(it))
            }
            .collect { value ->
                println(value)
            }
    }

限長操作符 take(2),表示最多接受兩條數(shù)據(jù)。
組合操作符:就像Kotlin標準庫中的Sequence,zip擴展函數(shù)一樣京腥,流擁有一個zip操作符用于組合兩個流中的相關(guān)值另伍。

fun collectFlow() = runBlocking<Unit> {
       val  nubs=(1..3).asFlow()
       val strs= flowOf("one","Two","Three")
        nubs.zip(strs){
            a,b->"$a ->$b"
        }.collect{
            println(it)
        }
    }

1、Channel實際上是一個并發(fā)安全的隊列,它可以用來連接協(xié)程摆尝,實現(xiàn)不同協(xié)程的
通信。channel默認容量1因悲。

fun ceshiChannel() = runBlocking<Unit> {
        //生產(chǎn)者
        val channel = Channel<Int>()
        val producer = GlobalScope.launch {
            var i = 0
            while (true) {
                delay(1000)
                channel.send(++i)
                println("send $i")
            }
        }
        //消費者
        val consumer = GlobalScope.launch {
            while (true) {
               val element=channel.receive()
                println("receive $element")
            }
        }
        joinAll(producer,consumer)
    }

②迭代Channel
Channel本身確實像序列堕汞,所以我們在讀取的時候可以直接獲取一個Channel的iterator.

fun ceshiChannel2() = runBlocking<Unit> {
        //生產(chǎn)者
        val channel = Channel<Int>()
        val producer = GlobalScope.launch {
            for (x in 1..5) {
                channel.send(x)
                println("send $x")
            }
        }
        //消費者
        val consumer = GlobalScope.launch {
            val iterator =channel.iterator()
            while (iterator.hasNext()) {
                val element=channel.receive()
                println("receive $element")
                delay(2000)
            }
            //也或者可以以下寫法
            for(element in channel){
                println("receive $element")
                delay(2000)
            }
        }
        joinAll(producer,consumer)
    }

③、produce與actor 構(gòu)造生產(chǎn)者與消費者的便捷方法
我們可以通過produce方法啟動一個生產(chǎn)者協(xié)程晃琳,并返回一個ReceiveChannel讯检,其他協(xié)程就可以用這個Channel來接收數(shù)據(jù)了。反過來卫旱,我們可以用actor啟動一個消費者協(xié)程

fun ceshiChannel3() = runBlocking<Unit> {
        //生產(chǎn)者
        val receiveChannel:ReceiveChannel<Int> = GlobalScope.produce {
            repeat(100){
                delay(1000)
                send(it)
            }
        }
        //消費者
        val consumer = GlobalScope.launch {
            for(element in receiveChannel){
                println("receive $element")
            }
        }
        joinAll(consumer)
    }
    fun ceshiChannel4() = runBlocking<Unit> {
        //消費者
        val sendChannel:SendChannel<Int> = GlobalScope.actor {
            while (true){
                val element=receive()
                println(element)
            }
        }
        //生產(chǎn)者
        val producer = GlobalScope.launch {
            for (x in 1..5) {
                sendChannel.send(x)
            }
        }
        producer.join()
    }

④人灼、Channel的關(guān)閉
produce和actor返回的Channel都會隨著對應(yīng)的協(xié)程執(zhí)行完畢而關(guān)閉,也正是這樣顾翼,Channel才被稱為熱數(shù)據(jù)流投放。
對于一個Channel,如果我們調(diào)用了它的close方法适贸,它會立即停止接收新元素灸芳,也就是說這時它的isClosedforSend會立即返回true。而由于Channel緩沖區(qū)的存在拜姿,這時候可能還有一些元素沒有被處理完烙样,因此要等所有的元素都被讀取之后isClosedForReceive才會返回true。
Channel的生命周期最好由主導(dǎo)方來維護蕊肥,建該由主導(dǎo)的一方實現(xiàn)關(guān)閉谒获。
⑤、BroadcastChannel
前面提到壁却,發(fā)送端和接收端在Channel中存在一對多的情形批狱,從數(shù)據(jù)處理本身來講,雖然有多個接收端儒洛,但是同一個元素只會被一個接收端讀到精耐。廣播則不然多個接收端不存在互斥行為。

  fun ceshiChannel5() = runBlocking<Unit> {
        //消費者 也可以通過下面的broadcast進行轉(zhuǎn)換
        val broadcastChannel= BroadcastChannel<Int>(Channel.BUFFERED)
//        val channel = Channel<Int>()
//        val broadcastChannel=channel.broadcast(3)
        val producer = GlobalScope.launch {
            List(3){
                delay(1000)
                broadcastChannel.send(it)
            }
            broadcastChannel.close()
        }
        //消費者
        List(3){index->
            GlobalScope.launch {
                val receiveChannel=broadcastChannel.openSubscription()
                for (x in receiveChannel) {
                    println(x)
                }
            }
        }.joinAll()
    }

⑥琅锻、await多路復(fù)用
兩個API分別從網(wǎng)絡(luò)和本地緩存獲取數(shù)據(jù)卦停,期望哪個先返回就先用哪個做展示。
⑦恼蓬、多個Channel復(fù)用
跟await類似惊完,會接收到最快的那個channel消息。
2处硬、Flow
①小槐、Flow異步流
Flow最主要的作用在于異步返回多個值,文件下載就是Flow最經(jīng)典的一個應(yīng)用場景。
冷流還是熱流
Flow 是冷流凿跳,什么是冷流?簡單來說件豌,如果 Flow 有了訂閱者 Collector 以后,發(fā)射出來的值才會實實在在的存在于內(nèi)存之中控嗜,這跟加載的概念很像茧彤。
與之相對的是熱流,StateFlow 和 SharedFlow 是熱流疆栏,在垃圾回收之前曾掂,都是存在內(nèi)存之中,并且處于活躍狀態(tài)的壁顶。
②珠洗、StateFlow是一個狀態(tài)容器式可觀察數(shù)據(jù)流,StateFlow是可以向其收集器發(fā)出當前狀態(tài)更新和新狀態(tài)更新若专。還可通過其value屬性讀取當前狀態(tài)值许蓖。
③、SharedFlow 會向從其中收集值的所有使用方發(fā)出數(shù)據(jù)富岳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛔糯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窖式,更是在濱河造成了極大的恐慌蚁飒,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萝喘,死亡現(xiàn)場離奇詭異淮逻,居然都是意外死亡,警方通過查閱死者的電腦和手機阁簸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門爬早,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人启妹,你說我怎么就攤上這事筛严。” “怎么了饶米?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵桨啃,是天一觀的道長。 經(jīng)常有香客問我檬输,道長照瘾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任丧慈,我火速辦了婚禮析命,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己鹃愤,他們只是感情好簇搅,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著昼浦,像睡著了一般馍资。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上关噪,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音乌妙,去河邊找鬼使兔。 笑死,一個胖子當著我的面吹牛藤韵,可吹牛的內(nèi)容都是我干的虐沥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼泽艘,長吁一口氣:“原來是場噩夢啊……” “哼欲险!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匹涮,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤天试,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后然低,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喜每,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年雳攘,在試婚紗的時候發(fā)現(xiàn)自己被綠了带兜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡吨灭,死狀恐怖刚照,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情喧兄,我是刑警寧澤无畔,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站繁莹,受9級特大地震影響檩互,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咨演,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一闸昨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦饵较、人聲如沸拍嵌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽横辆。三九已至,卻和暖如春茄猫,著一層夾襖步出監(jiān)牢的瞬間狈蚤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工划纽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脆侮,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓勇劣,卻偏偏與公主長得像靖避,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子比默,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 概念 實際就是kotlin官方提供的線程API,相當于AsyncTask 特性:非阻塞掛起幻捏,可掛起/恢復(fù)執(zhí)行 本質(zhì)...
    Peakmain閱讀 1,885評論 0 10
  • 目錄 [toc] 1、協(xié)程是什么 如果我們?nèi)ゾS基百科命咐,可以找到一段類似的話: 協(xié)程是一種非搶占式或者說協(xié)作式的計算...
    denny_z閱讀 1,189評論 0 5
  • 一.Flow是什么篡九? 官方文檔給予了一句話簡單的介紹: Flow — cold asynchronous stre...
    lllllittlep閱讀 2,115評論 0 0
  • 前言 本文是閱讀協(xié)程Flow的總結(jié)筆記。 什么是Flow Kotlin中的Flow API是可以更好的異步處理按順...
    取了個很好聽的名字閱讀 2,995評論 0 0
  • 前言 上周在內(nèi)部分享會上大佬同事分享了關(guān)于 Kotlin 協(xié)程的知識侈百,之前有看過 Kotlin 協(xié)程的一些知識瓮下,以...
    九心_閱讀 1,479評論 1 12