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

三星Galaxy S9/S9+(Android9 Pie)DownloadManager外部存储下载失败

解决三星Galaxy S9/S9+(Android 9)上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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:12:01