iT邦幫忙

2022 iThome 鐵人賽

DAY 22
0

為什麼要異步

快,再快點,我們都討厭等待

但同時執行多項任務是非常需要技巧的,我們要在意順序、資源,更中意的是如果沒管理好,每次執行的順序可能會不一樣,造成不同的副作用並出現問題,異步併發非常方便,但也需要很多技巧

那我們所能做的是什麼呢? 不要異步 徹底了解異步工具,在
Kotlin, coroutine 是非常好的選項,簡而言之 coroutine 可以讓我們用同步方式寫出異步代碼

在前一篇文章中,我們已經提過了coroutine,而在這篇我會做個正式的簡單介紹,畢竟 coroutine 也是個大主題,可以去看我去年寫的 30 天 coroutine

Kotlin coroutine

coroutine

在安卓裡,我們有很多方式實現異步 handler, thread, rx ...etc, 而最簡單的方式是 @runOnUiThread coroutine, 透過這個庫,我們能夠輕鬆的在不同線程切換任務


CoroutineScope(Job()).launch { //coroutinescope
    launch { //child coroutnescope
     //task   
    }
    
    async {//child coroutinescope
        //task
    }
    
}

在上面的程式碼中,我們創建了一個 CoroutineScope 類別,並在其 launch 裡面執行任務,如你所見,我們可以呼叫 launch or async 在 launch or async 裡面,而這會幫我們創建一個子 coroutineScope 於父 coroutineScope 之下,而 launch 和async的差異是

launch 建立一個子域,不回傳值
async 建立一個子域,並回傳值,但需要呼叫 await

async 的回傳值會包在 Deferred 裡面

val asyncString = async{
    "hello"
}
asyncString.apply { //Deferred<String>
    await()//String
}

suspend 掛起

coroutine 的核心之一就是 suspend, 他是一個函式修飾符,我們可以將它夾在函式前面

suspend fun get(){
    withContext(Dispatchers.IO){
        
    }
}

有了這個修飾符,我們就能一個超厲害的功能,看圖

沒錯,這個庫就會利用調配器,幫我們在不同線程之間調度任務,並在結束時返回原線程

dispatcher

現在我們知道了,我們要在 coroutinescope 裡面執行任務,而 suspend 的設計核心在切換線程,那調度氣勢要調度什麼呢

我們可以使用調度氣在不同線程裡面執行任務,但我們無法決定要哪一個實體,在底層實作,調度氣會為我們管理任務調度和線程管理,而我們總共有四種調度器

  1. io, 適合 io 任務
  2. main, 主線程,只有一個實例,而這個線程總是在更新畫面,所以不要阻塞他了
  3. default, 適合複雜任務,像是解析 json,壓縮圖片
  4. unconfine, 在當下的線程繼續執行

life of coroutinescope

CoroutineScope 的生命週期取決於他的 Job(),我們可以將其認作為每個 CoroutineScope 的 ID,而我們只能為根 coroutine 指定 Job,其他的 CoroutineScope 都會由套件指定

如果我們想要取消 coroutinescope,可以使用job.cancel(), 這會丟出一個 cancelException, 要執行這項操作後 CoruotineScope 實例才能被垃圾回收掉

再看看安卓的案例,我們總是在關心生命週期,官方也為此提供了工具 lifecycleScope and. viewModelScope, 我們可以用此特製 corotuinescope ,他們會在生命週期結束後自動取消

已上是基本的 coroutine 用法,想了解更多進階的請看這邊 link

English

why asynchronous

Quicker, faster, we all hate waiting,

but doing many things at the same time is tricky, we have to care about order, resources, and the most important thing is that tasks might run in different order each time, cause different side effects, cause errors under the hood, control multiple tasks is very difficult.

the best we can do is don't doing anything understand a async task tool and use it well, in Kotlin, coroutine is the best opinion, in short speaking coroutine allows us to do task async, but write code in sync way

in the previous article, we have an example in coroutine, but in this article, I will introduce it officially, or you can check those articles I wrote last year

Kotlin coroutine

coroutine

In Android, we have several ways to run async task, handler, thread, rx ...etc, the easiest way is coroutine, with this library, we can easily to switch task between task

a simple demo will look like this


CoroutineScope(Job()).launch { //coroutinescope
    launch { //child coroutnescope
     //task   
    }
    
    async {//child coroutinescope
        //task
    }
    
}

we will create a CoroutineScope class, and and run task inside launch, as you can see, we can implement launch or async inside it as well, both launch and async will create a child coroutinescope under it parent coroutinescope, the different between them is
launch will create a child scope, and do not return value
async will create a child scope, and return the last value by calling await

async will wrap the return value inside Deferred

val asyncString = async{
    "hello"
}
asyncString.apply { //Deferred<String>
    await()//String
}

suspend

The core of coroutine is suspend, it is a function modifier, we can add it before function

suspend fun get(){
    withContext(Dispatchers.IO){
        
    }
}

with this simple modifier, we now have a great feature, check this image

as you can see, the library using dispatcher switch task from main to io, and switch back

dispatcher

Now we know that, we need to run everything inside coroutinescope, and the design of suspend is to switch tasks between threads, then what is the dispatcher means?

we can use Dispatcher to switch task between thread, but we can't decide will specific one, under the hood, the Dispatcher will manage the task dispatch, there are four default Dispatcher we have

  1. io, suit for io task
  2. main, main thread, only one instance, and the thread always updating ui, don't block this one
  3. default, complexity task, such as parsing json, compress image
  4. unconfine, run on the current running thread

life of coroutinescope

The life of CoroutineScope up to its Job(), you can take it as an ID for each coroutinescope, each we can only specific job to a root coroutine, beside that, all the jobs will auto generate by os.

if we want to cancel a coroutinescope, we can use job.cancel(), which will throw a cancelException, by throwing this, the CoruotineScope instance could be recycle by garbage collect by OS

Take android as an example, we have to care about the lifecycle, the android official created a useful tool, lifecycleScope and. viewModelScope, we can easy create corotuinescope will cancel with the lifecycle in fragment, activity ot viewModel

the basic usage of coroutine is simple, for more advanced technique, please check out the link


上一篇
Day 21 多用組合 委託 Delegate
下一篇
Day 23 戀愛腦的整潔架構 clean architecture
系列文
Kotlin on the way31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言