当前,我正在Kotlin中为Android开发一个持久性库。现在我正要处理文件操作(读取,写入等),我想知道这样做的最佳方法是什么?首先,我不必在主线程上执行此操作即可阻止UI。其次,我必须确保所有操作均立即执行,并且不会因进程终止或设备重启而丢失任何内容。
我在开发者网站上查看了background guide,但现在我有些困惑。由于我不想为每个持久化操作启动Forground服务,因此WorkManager似乎是我的最佳解决方案。该文档指出:
WorkManager用于可延迟的任务-即不需要立即运行-并且即使应用程序退出或设备重新启动也需要可靠地运行。
这是问题所在:我的工作应立即执行,并且不依赖于系统事件,因此我不确定这是否是最佳方法。您觉得对我来说最好的解决方案是什么?
答案 0 :(得分:1)
您可以拥有一个存储库,公开用于处理I / O的suspend
函数。这是一个TextRepository
,可以从Uri
读取和写入文本(因为这是从将来的支持文件和Storage Access Framework Uri
值的书本样本改编而成的):
class TextRepository(context: Context) {
private val resolver: ContentResolver = context.contentResolver
suspend fun read(source: Uri) = withContext(Dispatchers.IO) {
try {
resolver.openInputStream(source)?.use { stream ->
StreamResult.Content(source, stream.readText())
} ?: throw IllegalStateException("could not open $source")
} catch (e: FileNotFoundException) {
StreamResult.Content(source, "")
} catch (t: Throwable) {
StreamResult.Error(t)
}
}
suspend fun write(source: Uri, text: String): StreamResult =
withContext(Dispatchers.IO) {
try {
resolver.openOutputStream(source)?.use { stream ->
stream.writeText(text)
StreamResult.Content(source, text)
} ?: throw IllegalStateException("could not open $source")
} catch (t: Throwable) {
StreamResult.Error(t)
}
}
}
private fun InputStream.readText(charset: Charset = Charsets.UTF_8): String =
readBytes().toString(charset)
private fun OutputStream.writeText(
text: String,
charset: Charset = Charsets.UTF_8
): Unit = write(text.toByteArray(charset))
sealed class StreamResult {
object Loading : StreamResult()
data class Content(val source: Uri, val text: String) : StreamResult()
data class Error(val throwable: Throwable) : StreamResult()
}
在这种情况下,我使用的是加载内容错误(LCE)模式,其中suspend
函数正在返回StreamResult
。 StreamResult.Content
包装读入的文本或刚写的文本。
然后,您可以使用某种ViewModel
函数来调用suspend
:
class MainMotor(repo: TextRepository) : ViewModel {
private val _results = MutableLiveData<StreamResult>()
val results: LiveData<StreamResult> = _results
fun read(source: Uri) {
_results.value = StreamResult.Loading
viewModelScope.launch(Dispatchers.Main) {
_results.value = repo.read(source)
}
}
fun write(source: Uri, text: String) {
_results.value = StreamResult.Loading
viewModelScope.launch(Dispatchers.Main) {
_results.value = repo.write(source, text)
}
}
}
在我的情况下,我按照MVI样式的方式将未经修改的StreamResult
路由到MutableLiveData
,以供UI使用。实际上,ViewModel
可能会将回购结果转换为UI可以更直接使用的内容,因此LiveData
可以是其他类型,而ViewModel
可以执行转换。 / p>