簡要
本文參考Kotlin Coroutines 那一兩件事情、CoroutineScope、Android官網
- Dispatchers.Main – 使用此調度程序可在 Android 主線程上運行協程。此調度程序只能用於與界面交互和執行快速工作。示例包括調用 suspend 函數,運行 Android 界面框架操作,以及更新 LiveData 對象。
- Dispatchers.IO – 此調度程序經過了專門優化,適合在主線程之外執行磁盤或網絡 I/O。示例包括使用 Room 組件、從文件中讀取數據或向文件中寫入數據,以及運行任何網絡操作。
- Dispatchers.Default – 此調度程序經過了專門優化,適合在主線程之外執行佔用大量 CPU 資源的工作。用例示例包括對列表排序和解析 JSON
這類似於“注入調度程序”最佳做法。通過使用 GlobalScope,您將對類使用的 CoroutineScope 進行硬編碼,而這會帶來一些問題:
提高硬編碼值。如果您對 GlobalScope 進行硬編碼,則可能同時對 Dispatchers 進行硬編碼。
這會讓測試變得非常困難,因為您的代碼是在非受控的作用域內執行的,您將無法控制其執行。
您無法設置一個通用的 CoroutineContext 來對內置於作用域本身的所有協程執行
launch:可啟動新協程而不將結果返回給調用方。任何被視為“一勞永逸”的工作都可以使用 launch 來啟動。
async:會啟動一個新的協程,並允許您使用一個名為 await 的掛起函數返回結果。
備註:
需要在app Bundle裡面新增
dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2-native-mt' }
1. CoroutineScope vs Thread
下面是使用Thread去倒數計時
val thread = Thread {
for (i in 10 downTo 1) {
try {
sleep(1000)
} catch (e: InterruptedException) {
e.printStackTrace()
return@Thread
}
runOnUiThread {
binding.tvShow.text = "count down $i ..."
}
}
runOnUiThread {
binding.tvShow.text = "Done!"
}
}
thread.start()
binding.btnTest.setOnClickListener {
thread.interrupt()
}
下面使用CoroutineScope功能與上方相同
//GlobalScope 繼承 CoroutineScope
//全域Scope,整個應用程式的生命週期,最頂級的
//使用他相當於使用靜態函數
//可以避免被提早結束
val job: Job = GlobalScope.launch(Dispatchers.Main) {
for (i in 10 downTo 1) {
binding.tvShow.text = "count down $i ..." // update text
delay(1000)
}
binding.tvShow.text = "Done!"
}
binding.btnTest.setOnClickListener {
job.cancel()
}
2. 繼承CoroutineScope使用job
class MainActivity : AppCompatActivity(), CoroutineScope { //job沒有特別定義的話 //launch,會跑在非MainThread,不可刷新UI private val job = Job() override val coroutineContext: CoroutineContext get() = job private fun runTimer() { launch { for (i in 10 downTo 1) { //不能刷新UI會閃退 //binding.tvShow.text = "count down $i ..." // update text delay(1000) } //不能刷新UI會閃退 //binding.tvShow.text = "Done!" } binding.btnTest.setOnClickListener { job.cancel() } } override fun onDestroy() { super.onDestroy() //只有取消這個job job.cancel() } }
3. CoroutineScope委託MainScope
//CoroutineScope委託MainScope //此時launch會跑在MainThread class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { override fun onDestroy() { super.onDestroy() //Scope cancel意味著全部取消 cancel() } private fun runTimer() { val job = launch { for (i in 10 downTo 1) { binding.tvShow.text = "count down $i ..." // update text delay(1000) } binding.tvShow.text = "Done!" } binding.btnTest.setOnClickListener { //job cancel意味著只取消這個工作 job.cancel() } } }
4. CoroutineScope搭配suspend
//CoroutineScope委託MainScope //此時launch會跑在MainThread class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { override fun onDestroy() { super.onDestroy() //Scope cancel意味著全部取消 cancel() } //suspend讓自己原先的執行緒暫停 //切換到Dispatchers.IO //處理完後,再回來原先的執行緒繼續往下作 suspend fun readSomeFile() = withContext(Dispatchers.IO) { //模擬處理一些資料 var j = 0 for(i in 0 until 1000000000) { j += 1 } } private fun runTimer() { val job = launch(Dispatchers.Main) { binding.tvShow.text = "123" binding.tvShow.visibility = View.VISIBLE readSomeFile() binding.tvShow.visibility = View.GONE } binding.btnTest.setOnClickListener { //job cancel意味著只取消這個工作 job.cancel() } } }
5. CoroutineScope+async+await
以下會打印result2
class MainActivity : AppCompatActivity(), CoroutineScope { private val job = Job() override val coroutineContext: CoroutineContext get() = job override fun onCreate(savedInstanceState: Bundle?) { val rawText: Deferred<String> = async(Dispatchers.IO) { "result2" } launch { //需放在此使用 val text = rawText.await() println(text) } //在此用會error //rawText.await() } override fun onDestroy() { super.onDestroy() //Scope cancel意味著全部取消 job.cancel() } }
6. runBlocking+async+suspend
以下會打印
The answer is 42
Completed in 1049 ms
private fun testFunction() = runBlocking {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
suspend fun doSomethingUsefulOne(): Int {
delay(1000L)
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L)
return 29
}
7. CoroutineScope+runBlocking
val mScope = object : CoroutineScope { override val coroutineContext: CoroutineContext get() = Job() } private fun testFunction() = runBlocking { //先打印runBlocking 2 //再打印runBlocking 1 mScope.launch (Dispatchers.Main){ delay(300) println("runBlocking 1") } mScope.launch (Dispatchers.Main){ delay(100) println("runBlocking 2") } Thread.sleep(1000) //Keep process alive } fun testFunction2() { //先打印runBlocking 1 //再打印runBlocking 2 runBlocking (Dispatchers.IO){ delay(300) println("runBlocking 1") } runBlocking (Dispatchers.IO){ delay(100) println("runBlocking 2") } Thread.sleep(1000) //Keep process alive } override fun onDestroy() { super.onDestroy() mScope.cancel() }
訂閱Codeilin的旅程,若有最新消息會通知。
廣告
發表迴響