Android R下载Base64编码PDF并调用预装查看器打开的问题求解
解决方案
你需要的无文件选择器、直接打开PDF的需求可以通过FileProvider实现,完美适配Android R及以上版本,具体操作步骤如下:
步骤1:配置FileProvider
在模块级AndroidManifest.xml的<application>节点中添加如下配置:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
步骤2:创建文件路径配置文件
在res/xml目录下新建file_paths.xml文件,内容如下(对应你存储PDF的外部应用私有下载目录):
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="app_downloads" path="Download/" /> </paths>
步骤3:修改原有逻辑代码
你第一版的代码只需调整2处即可正常运行:
- 文件名补
.pdf后缀,保证系统能识别文件格式 - 替换
Uri.fromFile为FileProvider生成的Content Uri,同时给外部PDF查看器授予临时读取权限
修改后的完整代码如下:
lifecycleScope.launch { withContext(Dispatchers.IO) { try { Snackbar.make(binding.root, getString(R.string.load_document_started), Snackbar.LENGTH_LONG).show() val documentData = DocumentDao().get(document.id, montageOrder) val docAsByte = Base64.decode(documentData.data, Base64.DEFAULT) val currentDateString = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) // 补全.pdf后缀 val fileName = document.documentType .lowercase() .replace("ä", "ae") .replace("ü", "ue") .replace("ö", "oe") + "_" + currentDateString + ".pdf" val file = File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName) val fileStream = FileOutputStream(file) fileStream.write(docAsByte) fileStream.close() val context = requireContext() val target = Intent(Intent.ACTION_VIEW) // 用FileProvider生成Content Uri,authorities需和manifest配置一致 val pdfUri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) target.setDataAndType(pdfUri, "application/pdf") // 授予外部应用临时读取权限 target.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY val intent = Intent.createChooser(target, "打开PDF") startActivity(intent) } catch (e: Exception) { Log.e(TAG, "Dokument konnte nicht geladen werden: " + e.message, e) Snackbar.make(binding.root, getString(R.string.exception_could_not_load_document), Snackbar.LENGTH_LONG).show() } } }
原有方案问题说明
- 第一版代码抛出
FileUriExposedException是因为Android 7.0及以上版本禁止应用直接向外部暴露file://类型的Uri,必须使用content://类型的Uri共享文件 - 第二版SAF方案的
CreatePdfDocument合约本身就会触发系统文件选择器,不符合无弹窗需求,且打开文件时未授予读取权限导致无法直接打开
内容的提问来源于stack exchange,提问作者Tommehh
相关产品推荐
相关产品推荐

