在 Android程序开发中如果需要下载文件,除了自己程序内部实现下载外,还可以直接使用 Android 系统自带的下载器进行下载,使用系统下载器通常有两种方式:

1. 浏览器下载

将下载链接使用浏览器打开,把下载任务交给浏览器,让浏览器调用系统下载器去下载,下载过程在通知栏有下载进度,下载完后文件通常存放在 “外部存储器” 根目录下的 download 文件夹, 也就是: /mnt/sdcard/download

打开下载链接的 Intent:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setData(Uri.parse("下载链接"));
startActivity(intent);

使用这种方法下载完全把工作交给了系统应用,自己的应用中不需要申请任何权限,方便简单快捷。但如此我们也不能知道下载文件的大小,不能监听下载进度和下载结果。

2. DownloadManager 系统服务

Android 2.3 (API 10) 以后,系统开放了内置下载器服务,也就是 DownloadManager,是专用于处理耗时长的 HTTP 文件下载的系统服务,在后台进行下载,并自动处理网络连接变化,失败重试。

通过 DownloadManager 我们可以在自己的程序中提交下载请求,在通知栏中可以自动显示下载进度展示通知进度条,可以指定下载文件的保存位置,并实时获取下载进度,监听下载结果。

DownloadManager 的实例通过 context.getSystemService(Context.DOWNLOAD_SERVICE) 获取,使用 DownloadManager 还必须要声明网络权限:android.permission.INTERNET;如果下载文件保存到外部存储器,还需要声明外部存储器的读写权限。

DownloadManager 中有两个重要的内部类:

DownloadManager.Request :封装一个下载请求添加到系统下载器队列。
DownloadManager.Query :查询下载任务,可实时获取下载进度,下载结果。

使用步骤:

1、配置权限

在 AndroidManifest.xml 配置权限:

<!-- 必须配置网络权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 如果将下载的文件保存到外部存储器,还需要配置外部存储器的读写权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2、封装下载请求(Request),加入下载队列

/** 1. 封装下载请求*/
// http 下载链接(该链接为 CSDN APP 的下载链接,仅做参考)
String downloadUrl = "http://apk.hiapk.com/appdown/net.csdn.csdnplus";
// 创建下载请求
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
/** 设置在通知栏是否显示下载通知(下载进度), 有 3 个值可选:*    VISIBILITY_VISIBLE:                   下载过程中可见, 下载完后自动消失 (默认)*    VISIBILITY_VISIBLE_NOTIFY_COMPLETED:  下载过程中和下载完成后均可见*    VISIBILITY_HIDDEN:                    始终不显示通知*/
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);// 设置通知的标题和描述
request.setTitle("通知标题XXX");
request.setDescription("对于该请求文件的描述");
/** 设置允许使用的网络类型, 可选值:*     NETWORK_MOBILE:      移动网络*     NETWORK_WIFI:        WIFI网络*     NETWORK_BLUETOOTH:   蓝牙网络* 默认为所有网络都允许*/
// request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 添加请求头
// request.addRequestHeader("User-Agent", "Chrome Mozilla/5.0");
// 设置下载文件的保存位置
File saveFile = new File(Environment.getExternalStorageDirectory(), "demo.apk");
request.setDestinationUri(Uri.fromFile(saveFile));
/** 2. 获取下载管理器服务的实例, 添加下载任务*/
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
// 将下载请求加入下载队列, 返回一个下载ID
long downloadId = manager.enqueue(request);
// 如果中途想取消下载, 可以调用remove方法, 根据返回的下载ID取消下载, 取消下载后下载保存的文件将被删除
// manager.remove(downloadId);

3、查询下载状态(Query)

添加一个下载请求(Request)到下载管理器的队列中,将返回一个下载ID,通过该ID可以实时查询到下载进度,成功与失败等状态。

// 获取下载管理器服务的实例
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
// 创建一个查询对象
DownloadManager.Query query = new DownloadManager.Query();
// 根据 下载ID 过滤结果
query.setFilterById(downloadId);
// 还可以根据状态过滤结果
// query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
// 执行查询, 返回一个 Cursor (相当于查询数据库)
Cursor cursor = manager.query(query);
if (!cursor.moveToFirst()) {cursor.close();return;
}
// 下载ID
long id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
// 下载请求的状态
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
// 下载文件在本地保存的路径(Android 7.0 以后 COLUMN_LOCAL_FILENAME 字段被弃用, 需要用 COLUMN_LOCAL_URI 字段来获取本地文件路径的 Uri)
String localFilename = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
// 已下载的字节大小
long downloadedSoFar = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
// 下载文件的总字节大小
long totalSize = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
cursor.close();
System.out.println("下载进度: " + downloadedSoFar  + "/" + totalSize);
/** 判断是否下载成功,其中状态 status 的值有 5 种:*     DownloadManager.STATUS_SUCCESSFUL:   下载成功*     DownloadManager.STATUS_FAILED:       下载失败*     DownloadManager.STATUS_PENDING:      等待下载*     DownloadManager.STATUS_RUNNING:      正在下载*     DownloadManager.STATUS_PAUSED:       下载暂停*/
if (status == DownloadManager.STATUS_SUCCESSFUL) {/** 特别注意: 查询获取到的 localFilename 才是下载文件真正的保存路径,在创建* 请求时设置的保存路径不一定是最终的保存路径,因为当设置的路径已是存在的文件时,* 下载器会自动重命名保存路径,例如: .../demo-1.apk, .../demo-2.apk*/System.out.println("下载成功, 打开文件, 文件路径: " + localFilename);
}

通常如果在自己的应用中需要显示下载进度,可以使用一个定时器,每隔1秒获取一次下载进度,然后根据自己的需求显示在界面上。

4、监听 点击通知 与 下载完成 的广播

上面查询下载状态的方式是自己主动轮询,监听下载完成更好的方式是监听系统下载服务发出的广播,DownloadManager 在用户点击了下载进度的通知栏 和 下载完成后 都会发出相应的广播。

广播实现:

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import java.util.Arrays;
public class DownloadManagerReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) {System.out.println("用户点击了通知");// 点击下载进度通知时, 对应的下载ID以数组的方式传递long[] ids = intent.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);System.out.println("ids: " + Arrays.toString(ids));} else if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {System.out.println("下载完成");/** 获取下载完成对应的下载ID, 这里下载完成指的不是下载成功, 下载失败也算是下载完成,* 所以接收到下载完成广播后, 还需要根据 id 手动查询对应下载请求的成功与失败.*/long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1L);System.out.println("id: " + id);// 根据获取到的ID,使用上面第3步的方法查询是否下载成功}}
}

在 AndroidManifest.xml 配置广播:

<receiver android:name="com.xiets.demo.DownloadManagerReceiver"><intent-filter><!-- 配置 点击通知 和 下载完成 两个 action --><action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/><action android:name="android.intent.action.DOWNLOAD_COMPLETE"/></intent-filter>
</receiver>

APP安装代码中添加兼容>7.0


if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0以下uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);installPackge(context,intentInstall,uri);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0-70File apkFile = queryDownloadedApk(context, completeDownLoadId);uri = Uri.fromFile(apkFile);installPackge(context,intentInstall,uri);
} else {//兼容7.0intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权File file= new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS),"app-release.apk");Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileProvider",file);installPackge(this,intentInstall,uri);
}

其中:
file路径:/storage/emulated/0/Download/app-release.apk

使用FileProvide得到uri为:content://com.demo.aiyang.demo.fileProvider/download/Download/app-release.apk
错误获取File路径代码如下:

//改正前写法File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app-release.apk");
//file路径:/storage/emulated/0/Android/data/com.demo.aiyang.demo/files/Download/app-release.apk
/改正后写法
File file= new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"app-release.apk");
//file路径:/storage/emulated/0/Download/app-release.apk

因为FileProvider不支持sdcard目录下的文件共享。

3、适配Android 8.0:未知来源的应用权限
Android 8.0的手机会出现在线更新不了新版本,华为荣耀V10手机测试apk下载完成后直接白屏提示“解析包时出现问题”。原因是Android8.0以上未知来源的应用是不可以通过代码来执行安装的(允许手动安装)。Google这么做是为了防止不合法APK安装侵犯了用户权益。适配如下:

(1) 在清单文件中申明权限

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

(2)监听apk下载状态的广播中添加代码:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0以下

  uri = downloadManager.getUriForDownloadedFile(completeDownLoadId);installPackge(context,intentInstall,uri);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 兼容6.0-70File apkFile = queryDownloadedApk(context, completeDownLoadId);uri = Uri.fromFile(apkFile);installPackge(context,intentInstall,uri);
} else {Log.i("aaa", ">7.0");nstallPackgeAPI28(context);// 兼容Android 8.0
}

兼容Android 8.0 是否有安装权限

   /***  兼容 8.0 未知来源应用安装*/int Code_INSTALLPACKAGES = 1;@RequiresApi(api = Build.VERSION_CODES.O)private void startInstallPermissionSettingActivity() {Uri packageURI = Uri.parse("package:" + getPackageName());Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);startActivityForResult(intent, Code_INSTALLPACKAGES);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == Code_INSTALLPACKAGES){InstallPackgeAPI28(this);}}private void InstallPackgeAPI28(Context context){Intent intentInstall = new Intent();intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权File file= new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"app-release.apk");Uri uri = FileProvider.getUriForFile(context, getPackageName() + ".fileProvider", file);boolean isInstallPermission = false;//是否有8.0安装权限if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {isInstallPermission = getPackageManager().canRequestPackageInstalls();if (isInstallPermission) {installPackge(this,intentInstall,uri);} else {new AlertDialog.Builder(this).setTitle("权限申请").setMessage("亲,没有权限我会崩溃,请把权限赐予我吧!").setPositiveButton("赏给你", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.cancel();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {startInstallPermissionSettingActivity();}}}).setNegativeButton("取消",null ).show();}}else{installPackge(this,intentInstall,uri);}}

4、适配Android 9.0:Https网络请求
前面代码基本上都已经算非常完整了,可以在大部分手机上都可以正常安装和使用。但是,我发现依然有很多网友留言说让我兼容9.0。于是乎,我自己也用9.0的测试手机运行了一下,可以下载安装啊!没问题啊!
呵呵。。。终究是我太大意了。 demo 里的 targetSdkVersion 并没有大于 API 28 Android 9.0 。因此,也就不会出现什么问题。审视一下一边代码就明白了,无非就是无法下载apk的问题。
Android P 9.0 的Https 如果使用http就是通过在AnroidManifest.xml中的application标签下设置如下属性即可。

android:usesCleartextTraffic="true"

DownloadManager的使用相关推荐

  1. Android APP更新下载工具类——简单封装DownloadManager

    几乎所有APP都包含了检查更新功能,更新下载功能的实现方式常用的有两种:1.使用App网络框架的文件下载请求:2.使用自带的DownloadManager类:本文介绍第二种,简单封装一下Downloa ...

  2. DownloadManager 的使用

    一.基本概念     1.DownloadManager是Android 2.3A (API level 9) 引入的,基于http协议,用于处理长时间下载. 2.DownloadManager对于断 ...

  3. [Android] DownloadManager下载管理类2.3新增API介绍

    从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到.下载管理类可以长期处理多个HTTP下载任务,客户端只需要给出请 ...

  4. android 自动更新 服务端,搭建android版本更新服务器使用android系统自带的DownloadManager下载文件...

    这几天想自己做一个文件更新的功能,但是由于不知道怎样写服务端,所以一直没有去做,后来发现原来服务端编写简直是太简单了,所以今天就实现了 版本更新的这样一个功能. 一搭建版本更新服务器: 搭建这个一个服 ...

  5. DownloadManager不好用?试试ZlsamDownloadService

    前一阵子,在给公司的智能电视做下载模块的时候发现,android自带的DownloadManager在有线网的情况下没有反应.看了下源码发现标准的手机版android的DownloaderManage ...

  6. Android开发笔记(六十一)文件下载管理DownloadManager

    下载管理DownloadManager 文件下载其实是网络数据访问的一种特殊形式,使用普通的http请求也能完成,就是实现起来会繁琐一些.因为下载功能比较常用,而且业务功能相对统一,所以从Androi ...

  7. 使用Android自带DownloadManager下载文件

    SDK在API Level 9中加入了DownloadManager服务,可以将长时间的下载任务交给系统,完全由系统管理. 直接看实例代码: [java]view plaincopy packagec ...

  8. [Android Pro] 判断Uri对应的ContentProvider所操作的数据库u存在,及DownloadManager的暂停,继续...

    reference to : http://blog.csdn.net/u012858313/article/details/38821857 项目中遇到一个问题,就是用到DownloadManage ...

  9. Android App自动更新解决方案(DownloadManager)

    Android App自动更新解决方案(DownloadManager) 参考文章: (1)Android App自动更新解决方案(DownloadManager) (2)https://www.cn ...

  10. Android DownloadManager 的使用

    分类: android 技巧2013-05-28 10:32 3278人阅读 评论(1) 收藏 举报 目录(?)[+] 从Android 2.3(API level 9)开始Android用系统服务( ...

最新文章

  1. Linux 内核启动流程
  2. AntD 官网样例 InputRef报错原因
  3. ASP.NET Core学习之五 EntityFrameworkCore
  4. php如何打印程序运行时间,php计算程序运行时间的简单实例 - microtime
  5. python自动导出数据脚本_使用python生成一个导出数据库的bat脚本文件的示例代码...
  6. NVIDIA解码器代码官方示例
  7. rzsz工具 源码交叉编译
  8. 一文带你详细了解光纤传感器
  9. 爬取飞猪IP免费代理练习
  10. 高分七号卫星发射成功
  11. 数据结构与算法——栈( Stack )
  12. Datawhale- DS- Jun - 第一章:第一节数据载入及初步观察-课程
  13. iOS 10版本适配
  14. c语言main的作用是什么,c语言main是什么意思-与非网
  15. 闲置kindle改为电子墨水屏时钟——本地静态页面无需联网
  16. UI设计中线面结合图标设计总结
  17. 【情暖寒冬 让爱同行】中创算力开展“寒冬送温暖”公益活动
  18. 怎么合并多个excel表
  19. 专访 iOS 技术专家孙源:开发者的成长始于“死磕”
  20. Last Day Of Summer

热门文章

  1. 二进制转十六进制详解
  2. mysql8.x实践系列(3)Qt客户端连接mysql报错:Authentication plugin ‘caching_sha2_password‘ reported error
  3. Qt中自定义控件拖拽,QT实现拖拽功能--小白友好版
  4. C/C++---中多继承构造函数调用顺序
  5. 在cmd中输入javac不行_cmd运行java可以javac不行
  6. OMRON plc连接电脑
  7. 爱莫科技荣获动点科技OTEC-EMERGE科技榜单新零售优秀企业
  8. 从request获取各种路径request.getRealPath()
  9. java nextval_kmp算法中的nextval实例解释
  10. 公务员—行测总体分析