三星Galaxy S9/S9+(Android9 Pie)DownloadManager外部存储下载失败
我之前也碰到过一模一样的情况——三星Android 9设备上用DownloadManager写外部SD卡死活失败,权限都给了路径也存在,其他设备却完全正常,折腾了好一阵才找到原因,给你分享下解决方案:
问题本质
三星在Android 9的ROM里给DownloadManager加了额外的权限限制:DownloadManager运行在系统进程,不会继承你的App申请的存储权限,哪怕你已经拿到了READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE,直接指定外部SD卡的自定义路径还是会被拒。
可行的解决办法
办法1:用系统默认的公共下载目录(最简单)
如果能接受文件存在系统Download文件夹里,直接用setDestinationInExternalPublicDir替换手动指定Uri就行,系统会自动处理DownloadManager的存储访问:
public void downloadFile(String url, String fileName) { DownloadManager mDownloadManager = (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI); request.setVisibleInDownloadsUi(true); // 下载到公共Download目录 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); mDownloadManager.enqueue(request); }
办法2:先下到内部存储再复制到目标路径(推荐,符合你的需求)
你的目标路径/storage/4295-DDD5/Android/data/com.example.myapplication/files/是App的专属外部存储目录,App本身对这个目录有完全访问权,只是DownloadManager写不了。那我们绕个弯:先让DownloadManager下载到App内部存储,再手动复制到外部SD卡的目标路径:
public void downloadAndMoveFile(String url, String targetFilePath) { // 第一步:先下载到App内部存储的临时文件 String tempFileName = "temp_download_" + System.currentTimeMillis() + ".file"; File tempFile = new File(context.getFilesDir(), tempFileName); DownloadManager mDownloadManager = (DownloadManager)context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI); request.setVisibleInDownloadsUi(true); request.setDestinationUri(Uri.fromFile(tempFile)); // 加入下载队列并记录ID long downloadId = mDownloadManager.enqueue(request); // 第二步:注册广播监听下载完成,完成后复制文件 BroadcastReceiver downloadReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long completedId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (completedId == downloadId) { // 下载完成,开始复制到目标路径 File targetFile = new File(targetFilePath); // 确保目标目录存在 targetFile.getParentFile().mkdirs(); try { // 复制文件 Files.copy(tempFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); // 清理临时文件 tempFile.delete(); } catch (IOException e) { e.printStackTrace(); // 这里可以加错误提示逻辑 } // 用完就注销接收器,避免内存泄漏 context.unregisterReceiver(this); } } }; // 注册广播接收器 context.registerReceiver(downloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); }
这个方法既满足了你要存到指定路径的需求,又绕开了三星的权限限制,亲测在S9上有效。
办法3:MediaStore(仅适用于媒体文件)
如果下载的是图片、视频、音频这类媒体文件,可以用setDestinationInExternalMediaDir或者通过MediaStore来管理路径,但普通文件不建议用这个方法,会增加复杂度。
验证小技巧
- 用办法1的话,直接打开系统的「下载」App就能看到文件
- 用办法2的话,下载完成后去Device File Explorer里检查目标路径,同时确认内部存储的临时文件已经被删除
内容的提问来源于stack exchange,提问作者Oagar

