之後四天,会讲讲以前面知识做基础开发时,会遇到的问题
在前面,讲到了coroutine是什麽,不妥善处理会有workleak,也介绍了他的exceptiom和取消,以及官方提供的lifecycleScope和viewModelScope
但是昨天coroutine托梦告诉我,他也想要细水长流的爱情呀,那些launch和async的任务都只是过客而已,利用完他就走了,心很累,我觉得吧,我们跟coroutine都甚麽交情了,这事肯定给她安排
那要application内还是application外,首先作为开发者,你要评估你任务的执行时间,如果你要执行的任务比application就应该选workManager
在这之前,我讲了许多coroutine的实用工具,也示范了一次性网路请情、socket连线、资料流的概述、paging3应用等等,但这些都只是为了在某个ui或某些情境的短期任务,当然并不是他不好,我也是参考了coroutine的最佳做法介绍的,只是长期任务,我们没讲到呀
那要如何为应用内的长期任务使用coroutine,其实答案已经讲出来了,就是在Application class内创建coroutine
class MyApp:Application() {
val applicationScope = CoroutineScope(SupervisorJob())
...
}
注意我们并不需要对此作出取消,因为现在的应用场景是这个corouitne应该要和application存活一样久
而现在,我们可以利用这个作用域,运行比调用作用域生命周期更长的任务
我直接跟官方blog借范例
//注入applicationScope
class Repository(
private val externalScope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher
) {
suspend fun doWork() {
withContext(ioDispatcher) {
doSomeOtherWork()
externalScope.launch {
// if this can throw an exception, wrap inside try/catch
// or rely on a CoroutineExceptionHandler installed
// in the externalScope's CoroutineScope
veryImportantOperation()
}.join()
}
}
}
//注入applicationScope
class Repository(
private val externalScope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher
) {
suspend fun doWork(): Any { // Use a specific type in Result
withContext(ioDispatcher) {
doSomeOtherWork()
return externalScope.async {
// Exceptions are exposed when calling await, they will be
// propagated in the coroutine that called doWork. Watch
// out! They will be ignored if the calling context cancels.
veryImportantOperation()
}.await()
}
}
}
上面的范例,即使viewModelScope已经destory了,在applicationScope的任务也会持续进行,并且doWork只会在veryImportantOperation完成後才回传
这个方法是这样被提出的,很多人觉得用withContext把任务丢去externalScope就可以了,但人生就没这麽简单
class Repository(
private val externalScope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher
) {
suspend fun doWork() {
withContext(ioDispatcher) {
doSomeOtherWork()
withContext(externalScope.coroutineContext) {
veryImportantOperation()
}
}
}
}
这个code会遇到两个问题
//withContext source code
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// compute new context
val oldContext = uCont.context
val newContext = oldContext + context
// always check for cancellation of new context
newContext.ensureActive()
在withContext里面,会将oldContext和newContext合并,以dispatcher而言,就是切换的动作,但如果像上面的范例呢,这个plus会把job一起换掉,然後就有问题了
⚠️ Disclaimer
If it turns out that the CoroutineContext of your applicationScope matches the GlobalScope or ProcessLifecycleOwner.get().lifecycleScope one, you can directly assign them as follows
class MyApplication : Application() {
val applicationScope = GlobalScope
}
博文给了免责申明,我也贴一下,如果你判断你的使用需求符合GlobalScope或ProcessLifecycleOwner,你也可以用
更多的细节请看博文
我这篇有讲到NonCancellable
另一个解法,是为了给在application外执行的任务,对workManager不熟悉的,请先看这篇,那要在workManager里使用coroutine,其实也非常简单,coroutine作为官方首选库,直接被包在一起,这边找最新版本
val work_version = "2.6.0"
// Kotlin + coroutines
implementation("androidx.work:work-runtime-ktx:$work_version")
那他被包的有多简单,首先继承自CoroutineWorker而不是Worker,可以看到doWork已经被包成suspend function了,而suspend的写法,也跟之前的一样
class CoroutineDownloadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
withContext(Dispatchers.IO) {
val data = downloadSynchronously("https://www.google.com")
saveData(data)
return Result.success()
}
}
}
对的,非常简单,其他的做法可以参考这篇workManager的github code sample
那coroutine的job要怎麽处理???
在workManager里面有个cancel(),可以取消或停止任务,而最棒的是,他会主动在coroutine丢出CancellationException去取消coroutine
coroutine:所以爱会消失对不对?我最後还是被取消了QQ
博文 coroutines-patterns-for-work-that-shouldnt-be-cancelled
WorkManager
>>: Day25 Plugin 从零开始到上架 - Android instagram APIs
回圈结构 - 使用for for回圈结构通常用於已知重复次数的方程序, 回圈结构中指定回圈变数的初始...
昨天我们提到红色框框里面的东西,今天就来根据他们的作用进行简单介绍吧! AppDelegate.sw...
哪个工程师人不想拥有一张帅气的云端架构图,本篇文章试着介绍 AWS, GCP 绘图工具及共通的特色。...
https://www.ithome.com.tw/news/135770 文中提到 Cortex-...
什麽是 Worker Pool Pattern? 设定好 pool 的 goroutine 数量,预...