如何在 Android(Java)中正确下载文件(适配 Android 11+)

浅敏君_5920

浅敏君_5920

2026-10-16

720人浏览

原创

如何在 android(java)中正确下载文件(适配 android 11+)

本文详解在 Android(尤其是 API 30+)中使用 Java 安全、可靠地下载网络文件的完整方案,涵盖 DownloadManager 失效原因、手动流式下载的正确实现、存储路径适配、权限处理及现代替代建议。

本文详解在 Android(尤其是 API 30+)中使用 Java 安全、可靠地下载网络文件的完整方案,涵盖 DownloadManager 失效原因、手动流式下载的正确实现、存储路径适配、权限处理及现代替代建议。

在 Android 11(API 30)及以上版本中,直接使用 DownloadManager 或旧式 File I/O 下载文件常遇到“无声失败”——无报错、无通知、文件不落地。根本原因在于:分区存储(Scoped Storage)强制启用 + 过时的存储路径 + 缺失运行时权限与配置。以下提供经过验证的可运行解决方案。

✅ 正确做法:使用 MediaStore 写入公共媒体目录(推荐,Android 10+)

Environment.getExternalStoragePublicDirectory() 已被弃用(API 29+),且在 Android 11+ 上即使声明权限也无法访问。正确方式是通过 MediaStore.Images.Media.EXTERNAL_CONTENT_URI 插入新条目,获取输出流:

private void downloadWithMediaStore(String title, String url) {
    // 1. 构建媒体插入参数
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, title);
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
    values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp");

    // 2. 获取输出流(自动处理路径与权限)
    Uri uri;
    try (ContentResolver resolver = getContentResolver()) {
        uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        if (uri == null) throw new IOException("Failed to create media entry");

        try (InputStream is = new URL(url).openStream();
             OutputStream os = resolver.openOutputStream(uri)) {

            byte[] buffer = new byte[8192];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        }
        Toast.makeText(this, "Download completed: " + title, Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        Log.e("Download", "Failed", e);
        Toast.makeText(this, "Download failed: " + e.getMessage(), Toast.LENGTH_LONG).show();
    }
}

✅ 优势:无需 WRITE_EXTERNAL_STORAGE 权限;文件自动出现在系统图库;兼容 Android 10–14;支持通知与状态监听(可扩展)。

⚠️ 若坚持使用 DownloadManager:关键修复点

你原代码失败的核心问题有三:

  • ❌ setDestinationUri() 指向私有目录(getExternalFilesDir()),DownloadManager 拒绝写入应用私有路径;
  • ❌ 缺少 android.permission.POST_NOTIFICATIONS(Android 12+ 必需);
  • ❌ Android 11+ 需显式声明 android:requestLegacyExternalStorage="false"(默认已生效),且目标路径必须为公共目录或 Content URI。

修正后的 DownloadManager 调用(仅适用于 Android 10 及以下,或需降级兼容):

private void downloadWithManager(String url, String fileName) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        // Android 11+ 不推荐,改用 MediaStore
        downloadWithMediaStore(fileName, url);
        return;
    }

    DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
    Uri uri = Uri.parse(url);

    DownloadManager.Request request = new DownloadManager.Request(uri)
            .setTitle("NASA APOD Image")
            .setDescription("Downloading...")
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, fileName)
            .setAllowedOverMetered(true)
            .setAllowedOverRoaming(true);

    manager.enqueue(request);
}

⚠️ 注意:setDestinationInExternalPublicDir() 在 Android 11+ 仍可用,但需确保 Manifest 中已声明:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS"></uses-permission><!-- Android 11+ 无需 WRITE_EXTERNAL_STORAGE -->

? 权限与清单配置(Android 11+ 必须)

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET"></uses-permission><uses-permission android:name="android.permission.POST_NOTIFICATIONS"></uses-permission><!-- Android 10+ 不再需要 READ/WRITE_EXTERNAL_STORAGE -->

? 运行时无需请求存储权限!MediaStore 和 DownloadManager 公共目录写入由系统授权。

? 总结与最佳实践

  • 优先使用 MediaStore API:安全、简洁、符合现代 Android 存储规范;
  • 避免 getExternalFilesDir() + DownloadManager 组合:这是静默失败的常见根源;
  • 勿依赖已废弃 API:如 Environment.getExternalStoragePublicDirectory();
  • 网络请求务必加异常捕获与用户反馈(Toast/Dialog),避免“黑盒”体验;
  • 测试真机环境:模拟器可能绕过部分存储限制,导致误判。

通过以上方案,你的 NASA APOD 图片将稳定下载至设备相册,并在系统图库中即时可见——这才是面向未来的 Android 文件下载实践。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

java android java安全

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10557

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7682

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6712

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1144

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

928

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1376

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2789

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20091

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1235

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习