Android 13环境下如何实现PDF账单下载至外部存储?
Android 13 下PDF账单下载实现方案
针对你的场景,这里有两种可行的实现方案,结合Android 13的存储权限规则,帮你理清最优选择:
方案一:继续使用DownloadManager API(推荐)
Android 13中,使用DownloadManager将文件下载到公共Downloads目录时,不需要申请任何存储权限,系统会自动处理文件写入的权限逻辑,完全适配你的需求。
具体实现步骤
- 构建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)
- 可选:监听下载完成事件,通过广播接收器通知用户:
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。以下是写入文件的实现步骤:
具体实现步骤
- 启动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)
- 在
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
相关产品推荐
相关产品推荐

