Make part of coroutine continue past cancellation
我有一个可以保存大文件的文件管理类。文件管理器类是一个应用程序单例,因此它比我的 UI 类寿命更长。我的 Activity/Fragment 可以从协程调用文件管理器的
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//In MyActivity: private fun saveTheFile() = lifecycleScope.launch { try { myFileManager.saveBigFile() myTextView.text ="Successfully saved file" } catch (e: IOException) { myTextView.text ="Failed to save file" } } //In MyFileManager withContext(Dispatchers.IO) { |
这种方法的问题是,如果 Activity 完成,我不希望保存操作被中止。如果活动在
我想要发生的是文件总是被保存。如果 Activity 仍然存在,那么我们可以在完成时显示 UI 更新。
我认为一种方法可能是像这样从挂起函数启动一个新的
1
2 3 |
suspend fun saveBigFile() = coroutineScope {
//… } |
我认为另一种选择可能是让这个函数成为一个常规函数,在完成时更新一些 LiveData。 Activity 可以观察结果的实时数据,并且由于 LiveData 在生命周期观察者被销毁时会自动删除它们,因此 Activity 不会泄漏到 FileManager。如果可以改用上述不那么复杂的方法,我想避免这种模式。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//In MyActivity: private fun saveTheFile() { val result = myFileManager.saveBigFile() result.observe(this@MyActivity) { myTextView.text = when (it) { true ->"Successfully saved file" else ->"Failed to save file" } } } //In MyFileManager |
你可以用
1
2 3 4 5 |
// May cancel here.
withContext(Dispatchers.IO + NonCancellable) { // Will complete, even if cancelled. } // May cancel here. |
如果您的代码的生命周期限定为整个应用程序的生命周期,那么这是
基本上可以说
1
2 3 4 5 6 |
@ObsoleteCoroutinesApi
val executor = GlobalScope.actor<() -> Unit>(Dispatchers.IO) { for (task in channel) { task() } } |
并像这样使用它:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
private fun saveTheFile() = lifecycleScope.launch {
executor.send { try { myFileManager.saveBigFile() withContext(Main) { myTextView.text ="Successfully saved file" } } catch (e: IOException) { withContext(Main) { myTextView.text ="Failed to save file" } } } } |
请注意,这仍然不是一个很好的解决方案,它会在其生命周期之后保留
我试过这个,它似乎可以按照我描述的那样做。 FileManager 类有自己的范围,但我想它也可以是 GlobalScope,因为它是一个单例类。
我们从协程在其自身范围内启动一项新工作。这是通过一个单独的函数完成的,以消除关于工作范围的任何歧义。我使用
为这个其他工作,所以我可以冒泡 UI 应该响应的异常。
然后在启动后,我们等待异步作业返回原始范围。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//In MyActivity:
private fun saveTheFile() = lifecycleScope.launch { try { myFileManager.saveBigFile() myTextView.text ="Successfully saved file" } catch (e: IOException) { myTextView.text ="Failed to save file" } } class MyFileManager private constructor(app: Application): suspend fun saveBigFile() { val deferred = saveBigFileAsync() private fun saveBigFileAsync() = async(Dispatchers.IO) { |
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/269076.html