后台的持久性文件操作

时间:2019-05-30 13:14:53

标签: android android-background

当前,我正在Kotlin中为Android开发一个持久性库。现在我正要处理文件操作(读取,写入等),我想知道这样做的最佳方法是什么?首先,我不必在主线程上执行此操作即可阻止UI。其次,我必须确保所有操作均立即执行,并且不会因进程终止或设备重启而丢失任何内容。

我在开发者网站上查看了background guide,但现在我有些困惑。由于我不想为每个持久化操作启动Forground服务,因此WorkManager似乎是我的最佳解决方案。该文档指出:

  

WorkManager用于可延迟的任务-即不需要立即运行-并且即使应用程序退出或设备重新启动也需要可靠地运行。

这是问题所在:我的工作应立即执行,并且不依赖于系统事件,因此我不确定这是否是最佳方法。您觉得对我来说最好的解决方案是什么?

1 个答案:

答案 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函数正在返回StreamResultStreamResult.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>