Android 10中DownloadManager下载PDF后打开异常问题求助
Android 10下载PDF后打开异常问题解决
问题现象
使用DownloadManager从服务器下载文件,通过BroadcastReceiver触发打开操作,在Android 12上运行正常,但Android 10中PDFViewer会先显示空白几秒,随后自动返回应用。
可能原因
- 文件下载完成广播触发时,文件尚未完全写入磁盘,导致PDF阅读器读取失败
- Android 10及以下版本中,临时Uri权限的授予机制存在差异,仅设置
FLAG_GRANT_READ_URI_PERMISSION可能无法让第三方应用正常获取权限 - 广播接收器注销时机过早,或者文件路径处理存在错误
解决方案
1. 修改下载完成后的处理逻辑
调整代码,确保文件写入完成后再打开,同时适配权限授予机制:
fun Activity.openLink(url: String) { val downloadmanager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val fileName = "file.pdf" val request = DownloadManager.Request(Uri.parse(url)) .setTitle("") .setDescription("") .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) downloadmanager.enqueue(request) val onComplete: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE) { val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0) val query = DownloadManager.Query().setFilterById(downloadId) // 使用use自动关闭Cursor,避免资源泄漏 downloadmanager.query(query).use { cursor -> if (cursor.moveToFirst()) { val columnStatusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS) if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnStatusIndex)) { val columnUriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI) val localUri = Uri.parse(cursor.getString(columnUriIndex)) val file = File(localUri.path!!) // 等待文件完全写入磁盘 waitForFileReady(file) val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.fileprovider", file) val target = Intent(Intent.ACTION_VIEW).apply { setDataAndType(uri, "application/pdf") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } // 给所有支持该Intent的应用手动授予读取权限(适配Android 10) val resInfoList = context.packageManager.queryIntentActivities(target, PackageManager.MATCH_DEFAULT_ONLY) resInfoList.forEach { resolveInfo -> val packageName = resolveInfo.activityInfo.packageName context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(Intent.createChooser(target, "")) } } } // 处理完成后注销广播接收器,捕获重复注销异常 try { context.unregisterReceiver(this) } catch (e: IllegalArgumentException) { e.printStackTrace() } } } // 等待文件可读取,最多等待5秒 private fun waitForFileReady(file: File) { val maxWaitTime = 5000 val checkInterval = 200 var waitedTime = 0 while (!file.canRead() && waitedTime < maxWaitTime) { Thread.sleep(checkInterval.toLong()) waitedTime += checkInterval } } } registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) }
2. 统一FileProvider配置
确保AndroidManifest.xml中的authorities与代码一致,避免权限匹配失败:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${BuildConfig.APPLICATION_ID}.fileprovider" android:exported="false" android:grantUriPermissions="true" tools:replace="android:authorities"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_filepaths" tools:replace="android:resource" /> </provider>
3. 简化FileProvider路径配置
res/xml/file_provider_filepaths.xml无需重复配置外部存储路径,保留一条即可:
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="."/> </paths>
修改说明
- 用
use自动管理Cursor生命周期,避免资源泄漏 - 添加文件就绪等待逻辑,确保文件完全写入后再打开
- 手动给所有支持PDF查看的应用授予Uri读取权限,适配Android 10的权限规则
- 调整广播接收器注销时机,确保业务逻辑执行完成后再注销
- 统一FileProvider的authorities配置,避免权限不匹配
内容的提问来源于stack exchange,提问作者Rasul Mingazov
相关产品推荐
相关产品推荐

