【发布时间】:2020-02-10 17:29:15
【问题描述】:
我有一个可以保存大文件的文件管理类。文件管理器类是一个应用程序单例,因此它比我的 UI 类寿命更长。我的 Activity/Fragment 可以从协程调用文件管理器的savesuspend 函数,然后在 UI 中显示成功或失败。例如:
//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
suspend fun saveBigFile() {
//Set up the parameters
//...
withContext(Dispatchers.IO) {
//Save the file
//...
}
}
这种方法的问题是,如果 Activity 完成,我不希望保存操作被中止。如果活动在withContext 块开始之前被销毁,或者withContext 块中有任何暂停点,则保存不会完成,因为协程将被取消。
我想要发生的是文件总是被保存。如果 Activity 仍然存在,那么我们可以在完成时显示 UI 更新。
我认为一种方法可能是从这样的挂起函数开始一个新的coroutineScope,但是当它的父作业被取消时,这个范围似乎仍然被取消。
suspend fun saveBigFile() = coroutineScope {
//...
}
我认为另一种选择可能是使其成为一个常规函数,在完成时更新一些 LiveData。 Activity 可以观察结果的实时数据,并且由于 LiveData 在生命周期观察者被销毁时会自动删除它们,因此 Activity 不会泄漏到 FileManager。如果可以做像上面这样不那么复杂的事情,我想避免这种模式。
//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
fun saveBigFile(): LiveData<Boolean> {
//Set up the parameters
//...
val liveData = MutableLiveData<Boolean>()
MainScope().launch {
val success = withContext(Dispatchers.IO) {
//Save the file
//...
}
liveData.value = success
}
return liveData
}
【问题讨论】:
-
可能你应该在服务中做这样的工作,即使你的活动/应用程序被杀死也需要完成。因为协程就像线程一样,其生命周期取决于它运行的进程。服务使您保证您的工作将完成并且不会受到活动/应用被终止的影响。
-
一个服务也在你的应用程序中运行,因此不能保证它不会被杀掉。如果你把它设为前台服务,它就不太可能被杀死。但我从未见过任何应用程序启动前台服务只是为了保存文件。很可能,文件操作不会花费足够长的时间,不会有应用程序被拆除以节省 RAM 的风险。
-
另一种方法是使用不会受到应用终止或重启影响的 workmanager。
标签: android kotlin kotlin-coroutines