是否可以从Android API 28的数据库获取的Uri打开PDF文件?



我正在为Android(>= API 28)编写文件共享应用程序,我有以下用例:

  1. 用户从其本地存储上传文件。
  2. 用户关闭应用,并在一段时间后重新打开它,导致应用向服务器请求文件列表以填充布局。
  3. 用户单击文件以预览它。

在这一点上,最直观的步骤是,鉴于应用程序知道文件URL,只需将其加载到指向即Google Docs的https://docs.google.com/viewerng/viewer?url=$myFileUrl或任何其他文件查看服务的WebView中即可。

事实是,我不希望我的应用程序过度依赖外部服务,所以我认为在文件上传(步骤 1)的那一刻,附加一个content://Uri并将其存储在数据库中以在步骤 3 中使用它。

这样,如果用户尚未重命名和移动他上传的文件,应用可以尝试在本地打开文件,从而节省一些带宽使用量。

正如@CommonsWare在本文中所写,应用程序有权访问Uri的时间是有限的;就我而言,它只持续到用户关闭应用程序。这意味着使用以下策略:

MainActivity.kt

// Getting the content:// Uri after a call to the server.
viewModel.selectedUrl.observe(this@MainActivity) { uri ->
val newIntent = Intent(Intent.ACTION_VIEW)
try {
newIntent.data = Uri.parse(uri)
newIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
newIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
try {
startActivity(newIntent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
}
// ...
}

仅当文件上传并立即打开时才有效。是否有可能以任何方式解决此问题,或者我应该坚持使用不涉及任何UriWebView选项?

更新:文件是如何上传的?

在我的MainActivity中,我有一个 FAB,单击它时会触发以下代码片段:

MainActivity.kt

val fileResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
viewModel.uploadFile(result, false) // This is the Uri.
}
fileFab.setOnClickListener {
val intent = Intent()
intent.type = "application/*"
intent.action = Intent.ACTION_OPEN_DOCUMENT
fileResultLauncher.launch(intent)
}

。在各自的ViewModel.ktRepository.kt调用之后,它最终会使用该方法进入Server类:

服务器.kt

override fun uploadFile(uri: Uri, title: String, isSound: Boolean, callback: IFileUploadCallback) {
Timber.d("Uploading document...")
val inputStream = c.contentResolver.openInputStream(uri)
var fileData: ByteArray? = null
inputStream?.buffered()?.use {
fileData = it.readBytes()
}
fileData?: return
// Call to Volley
// ...

感谢@blackapps和@CommonsWare,我在 ViewModel 类中缺少以下行:

context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)

ViewModel.kt

fun uploadFile(result: ActivityResult) {
if (result.resultCode == Activity.RESULT_OK) {
val uri: Uri = result.data?.data!!
context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
viewModelScope.launch(Dispatchers.IO) {
repository.uploadFile(uri, object: Server.IFileUploadCallback {
override fun onSuccess(fileUrl: String, content: String) {
uploadedFileUrl.postValue(RecyclerViewItem.Document(fileUrl, content, uri))
}
})
}
}
}

最新更新