Android11打开PDF报ActivityNotFoundException及Firestore PDF下载适配分区存储问题
问题1:Android 11 打开PDF报ActivityNotFoundException的解决
直接原因是旧代码存在笔误:你为pdfIntent设置了数据和类型,但最终调用context.startActivity()时传入的是未初始化的intent变量,导致Intent参数无效。
完整修复步骤:
- 修正笔误,统一使用
pdfIntent变量 - 启动前校验系统是否有支持打开PDF的应用,避免崩溃
- 确认FileProvider配置正确,
res/xml/file_paths.xml需要包含对应路径的配置:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="external_files" path="Documents/" /> <!-- 后续内部存储使用的话加这行 --> <files-path name="internal_files" path="." /> </paths>
修复后的旧方案打开方法:
suspend fun getPDFFileAndOpen(fileName: String) { val targetFileName = "$fileName.pdf" withContext(Dispatchers.IO) { val fileList = context.getExternalFilesDir(downloadDirectory)?.listFiles() val file = fileList?.find { it.name == targetFileName } ?: return@withContext val pdfUri = FileProvider.getUriForFile(context, authority, file) pdfIntent.setDataAndType(pdfUri, "application/pdf") // 校验是否有可处理的应用 if (pdfIntent.resolveActivity(context.packageManager) != null) { context.startActivity(pdfIntent) } else { // 此处添加提示用户安装PDF阅读器的逻辑 } } }
问题2:不使用DownloadManager的完整实现方案
你代码中创建的File(context.filesDir, "$fileName.pdf")本身就是应用专属内部存储的持久化文件,不需要额外的保存步骤,下载完成后就已经永久存储在应用内部,除非用户卸载应用或者手动清除应用数据。
完整实现代码:
class PDFDownloaderWithoutManager(private val context: Context) { private val authority = "${BuildConfig.APPLICATION_ID}.fileprovider" suspend fun downloadPDF(uri: StorageReference, fileName: String) { withContext(Dispatchers.IO) { // 直接创建内部存储文件,下载完成即持久化保存 val targetFile = File(context.filesDir, "$fileName.pdf") // 直接将Firestore文件下载到目标路径,不需要额外临时文件 uri.getFile(targetFile).await() // 如果需要额外保存到公共下载目录对其他应用可见,可以调用下面的方法 // saveToPublicDownload(targetFile, fileName) } } suspend fun getPDFFileAndOpen(fileName: String) { withContext(Dispatchers.IO) { val targetFile = File(context.filesDir, "$fileName.pdf") if (!targetFile.exists()) { // 处理文件不存在的逻辑 return@withContext } val pdfUri = FileProvider.getUriForFile(context, authority, targetFile) val pdfIntent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(pdfUri, "application/pdf") flags = Intent.FLAG_ACTIVITY_NEW_TASK addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } if (pdfIntent.resolveActivity(context.packageManager) != null) { context.startActivity(pdfIntent) } else { // 提示用户安装PDF阅读器 } } } // 可选:保存到公共下载目录,适配分区存储,不需要申请存储权限 private fun saveToPublicDownload(sourceFile: File, fileName: String) { val contentValues = ContentValues().apply { put(MediaStore.Downloads.DISPLAY_NAME, "$fileName.pdf") put(MediaStore.Downloads.MIME_TYPE, "application/pdf") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { put(MediaStore.Downloads.IS_PENDING, 1) } } val resolver = context.contentResolver val targetUri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) ?: return resolver.openOutputStream(targetUri)?.use { outputStream -> sourceFile.inputStream().use { inputStream -> inputStream.copyTo(outputStream) } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { contentValues.clear() contentValues.put(MediaStore.Downloads.IS_PENDING, 0) resolver.update(targetUri, contentValues, null, null) } } }
关于uri.getFile("URI TO SAVE FILE TO")方法的说明:这个方法接收的是本地文件的Uri,如果你要直接下载到MediaStore提供的公共目录Uri,可以直接传入上面saveToPublicDownload方法中创建的targetUri,不需要先创建内部存储的文件。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

