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

Android 13环境下如何实现PDF账单下载至外部存储?

Android 13 下PDF账单下载实现方案

针对你的场景,这里有两种可行的实现方案,结合Android 13的存储权限规则,帮你理清最优选择:

方案一:继续使用DownloadManager API(推荐)

Android 13中,使用DownloadManager将文件下载到公共Downloads目录时,不需要申请任何存储权限,系统会自动处理文件写入的权限逻辑,完全适配你的需求。

具体实现步骤

  1. 构建DownloadManager请求,指定S3的文件URL,设置下载路径为公共Downloads目录:
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
val uri = Uri.parse("你的S3账单PDF地址")
val request = DownloadManager.Request(uri)
    .setTitle("账单PDF")
    .setDescription("正在下载你的薪资账单")
    // 指定下载到公共Downloads目录,自定义文件名
    .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "薪资账单_${System.currentTimeMillis()}.pdf")
    .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)

// 发起下载请求
val downloadId = downloadManager.enqueue(request)
  1. 可选:监听下载完成事件,通过广播接收器通知用户:
class DownloadCompleteReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val downloadId = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) ?: -1
        if (downloadId != -1) {
            // 处理下载完成逻辑,比如提示用户文件位置
            Toast.makeText(context, "账单已下载到Downloads目录", Toast.LENGTH_SHORT).show()
        }
    }
}

记得在Manifest中注册广播接收器(或动态注册):

<receiver android:name=".DownloadCompleteReceiver">
    <intent-filter>
        <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
    </intent-filter>
</receiver>

这个方案优势是无需权限申请,代码简洁,用户能在系统默认的Downloads目录找到文件,完全适配Android 13的权限规则。

方案二:使用Storage Access Framework(SAF)(按需选择)

如果你需要让用户自定义存储位置(比如让用户选择保存到其他文件夹),才需要使用SAF。以下是写入文件的实现步骤:

具体实现步骤

  1. 启动SAF的文件选择器,让用户选择保存的位置和文件名:
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = "application/pdf"
    putExtra(Intent.EXTRA_TITLE, "薪资账单_${System.currentTimeMillis()}.pdf")
}
startActivityForResult(intent, REQUEST_CODE_SAVE_PDF)
  1. 在onActivityResult(或AndroidX的registerForActivityResult)中获取用户选择的Uri,然后写入文件:
// AndroidX方式示例
val savePdfLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        result.data?.data?.let { uri ->
            // 从S3下载文件流,写入到SAF返回的Uri中
            lifecycleScope.launch {
                try {
                    val inputStream = URL("你的S3账单PDF地址").openStream()
                    contentResolver.openOutputStream(uri)?.use { outputStream ->
                        inputStream.copyTo(outputStream)
                        Toast.makeText(this@MainActivity, "账单已保存", Toast.LENGTH_SHORT).show()
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    Toast.makeText(this@MainActivity, "保存失败", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

// 点击下载按钮时触发
savePdfLauncher.launch(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = "application/pdf"
    putExtra(Intent.EXTRA_TITLE, "薪资账单_${System.currentTimeMillis()}.pdf")
})

使用SAF时,不需要申请存储权限,因为用户通过系统选择器授予了特定Uri的写入权限,完全符合Android 13的隐私规则。

总结

  • 如果只需要默认下载到Downloads目录,优先用DownloadManager,实现简单且无需权限。
  • 如果需要让用户自定义存储位置,再使用SAF。

内容的提问来源于stack exchange,提问作者Juan Gil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:12:41