--- tags: Android --- # Kotlin Corourtine in Android ## Coroutine 什麼是Coroutine? 先來暸解這個字是怎麼來的。其實它是由兩個字所組合而來,分別是cooperation(合作)以及routine(例行作業)這兩個字組合起來,其實就可以大致上理解它是合作的例行作業?吧,其實我個人倒是覺得講合作我能理解,至於例行作業我還在努力參透其中的意義,說不定某天會突然開竅(疑?) ## 輕量級Thread? 可以在網路上找到很多的說明,說它是輕量級的Thread。它能做的事情跟Thread一樣,能夠進行異步處理耗時的工作,但是我認為它其實不算是Thread。與其說是一個輕量級的Thread,不如說它是自動協助開發者控制Thread的物件,透過有效的控制達到消耗資源最少卻又同時達到目的的效果。  參考上圖,我們先來點Android異步操作的基本慨念。基本上我們都知道在android中,執行耗時的工作在UI Thread(Main Thread)中處理是被禁止的,需要處理耗時的工作就必須透過IO Thread,就如同上圖一樣。而在Coroution的操作中就帶很強烈的帶入了這樣的操作概念,可以透過區塊宣告的方式說明現在我想要執行在IO Thread中,什麼時候我想要切回UI Thread中,在實際撰寫與閱讀上就可以很直覺的知道,這點我個人是覺得非常棒。 ## Coroutine 起手式 ### Dependencies 先在Gradle中新增相關的dependencies。 ``` =android //kotlin coroutines implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" ``` 在使用上我個人覺得真的很簡便,概念上就像是宣告一個程式碼區塊是指定給coroutine執行專用的,就如同下方: ```=android fun coroutineDemo() { CoroutineScope(Dispatchers.IO).launch { } } ``` CoroutineScope代表協程運作的區塊,可以作用在IO或是Main Thread中,視需要自行設定。CoroutineScope內有一個CoroutineContext,這個指的是運作情境,可以透過Dispatchers指定,如上面程式碼所示。除了CoroutineScope還有一個常見的就是GlobalScope,這個Scope繼承CoroutineScope,他的生命週期跟隨者Appication,所以不會被隨意取消掉,一般建議使用上用CoroutineScope就可以了。 以下,我建立幾個方法用來模擬耗時的工作: ```=android fun sleep1000(): String { Thread.sleep(1000) return "Sleep1000" } fun sleep2000(): String { Thread.sleep(2000) return "Sleep2000" } fun sleep3000(): String { Thread.sleep(3000) return "Sleep3000" } ``` 接著我們在scope中透過async這個方法來執行其中一個耗時工作: ```=android=1 CoroutineScope(Dispatchers.IO).launch { var deferred: Deferred<String> = async { sleep1000() } withContext(Dispatchers.Main) { textView.text = deferred.await() } } ``` 說明一下Coroutine裡面幾個關鍵字: - **runblock**: 啟動一個協程,並且block目前的Thread等待此協程執行完畢。 - **launch**: 在不阻塞目前thread的情況下,啟動協程並return一個Job物件。(該Job物件可以用來控制協程。) - **wichContext**: 指定協程要運作的情境(Main or IO Thread)。 - **async**: 在不阻塞目前thread的情況下,啟動協程並且return一個Deferred物件,需要配合await一起使用。 所以根據上面的程式碼,可以這樣解讀: 第1行 = 宣告一個coroutine的執行環境,並且要在IO Thread中執行。 第2行 = 啟動協程,執行sleep1000方法,並且我要回傳一個Defferrd物件。 第3行 = 宣告一個執行在Main Thread的區塊。 第4行 = 宣告textView.text獲得deferrd待sleep1000執行完所得到的結果。 是不是這樣看起來程式碼相當直觀?可以很線性的筆直閱讀下來。在這裡的async會在執行的時候直接先回傳一個deferred,並且該deferred物件會等待執行的結果取得後,再接著往下執行程式碼。 ## 多個協程 如果同時執行多個協程可以嗎?當然可以。在scope中可以啟動多個斜程來執行耗時的工作。可以向下方這樣的寫法: ```=android= CoroutineScope(Dispatchers.IO).launch { var deferred: Deferred<String> = async { sleep1000() } var deferred1: Deferred<String> = async { sleep2000() } var deferred2: Deferred<String> = async { sleep3000() } withContext(Dispatchers.Main) { textView.text = deferred.await()+deferred1.await()+deferred2.await() } } ``` 執行結果如下:  根據上面程式碼,在第6行可以把所有結果串起來,待最一個拿到回傳結果的deferred完成後,程式碼接著往下執行。 另外也可以把deferred寫在其他的協程裡面,讓協程等待deferred完成後再接著往下執行。 ```=android=1 CoroutineScope(Dispatchers.IO).async { var deferred: Deferred<String> = async { sleep1000() } var deferred1: Deferred<String> = async { deferred.await()+sleep2000() } var deferred2: Deferred<String> = async { deferred1.await()+sleep3000() } withContext(Dispatchers.Main) { textView.text = deferred2.await() } } ``` 執行果如下:  可以從上面的結果知道這樣總共需要執行6秒的時間才會完成所有的操作。 ## corourtine的操控 上面介紹了coroutine的一些很基本的用法,但是如果今天我想要對某種情境下中斷停止協程的話因該怎麼做呢?上面有說到launch這個方法會回傳一個Job物件,要操控協程,就得靠它了。請參考下面展示的一段程式碼: ```=android=1 private lateinit var countTimeJob : Job ... ``` ```=android=20 fun createCountTimeCoroutineScope() { countOver = false countTimeJob = CoroutineScope(Dispatchers.IO).launch { var i = 0; while (true) { if(countOver){ cancel() //可以在內部取消 } delay(1000) i++ withContext(Dispatchers.Main){ tvCountTime.text = i.toString() } } } } fun cancelCoroutine(){ countTimeJob.cancel() //可以在外部取消 } ``` 當然除了cancel這個取消的方法以外,還有以下的方法可以調用: - **join()**: 等待現在正在執行的job完成後,接著執行。 - **cancalAndJoin()**: 其實就是取消正在執行的協程,重新執行協程。 - **isActive()**: 確認協程是否在執行中。 以上就是coroutine一些簡單的基本用法。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up