You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 10中DownloadManager下载PDF后打开异常问题求助

Android 10下载PDF后打开异常问题解决

问题现象

使用DownloadManager从服务器下载文件,通过BroadcastReceiver触发打开操作,在Android 12上运行正常,但Android 10中PDFViewer会先显示空白几秒,随后自动返回应用。

可能原因

  1. 文件下载完成广播触发时,文件尚未完全写入磁盘,导致PDF阅读器读取失败
  2. Android 10及以下版本中,临时Uri权限的授予机制存在差异,仅设置FLAG_GRANT_READ_URI_PERMISSION可能无法让第三方应用正常获取权限
  3. 广播接收器注销时机过早,或者文件路径处理存在错误

解决方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:36:20