Android 经典笔记之五:DownloadManager下载管理器介绍

DownloadManager下载管理器介绍
目录介绍:

  • 0.简单介绍
  • 1.所需权限
  • 2.获取对象,开始下载
  • 3.取消下载
  • 4.Request类的介绍
    • 4.5.指定下载的类型
    • 4.6.定制Notification样式
    • 4.7.设置下载文件类型
    • 4.8.添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等
  • 5.Query 类
  • 6.不足之处
  • 7.代码案例【简易】

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

0.简单介绍

  • 关于DownloadManager简单介绍
    DownloadManager是android2.3以后,系统下载的方法,是处理长期运行的HTTP下载的系统服务。客户端可以请求的URI被下载到一个特定的目标文件。客户端将会在后台与http交互进行下载,或者在下载失败,或者连接改变,重新启动系统后重新下载。还可以进入系统的下载管理界面查看进度。DownloadManger有两个内部类,Request 和Query。Request类可设置下载的一些属性。Query类可查询当前下载的进度,下载地址,文件存放目录等数据。

1.所需权限

<uses-permission android:name="android.permission.INTERNET" />;
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>;

注意,访问本地控件【sd卡,手机内存卡】需要检测是否添加权限

/**检测存储权限*/
private final int REQUEST_EXTERNAL_STORAGE = 1;
private String[] PERMISSIONS_STORAGE = {
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
};

public void verifyStoragePermissions(Activity activity) {
    // Check if we have write permission
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (permission != PackageManager.PERMISSION_GRANTED) {
        // We don't have permission so prompt the user
        ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
    }
}

2.获取对象,开始下载

  • 获取对象,开始下载
DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
long id = downloadManager.enqueue(request);
//每下载的一个文件对应一个id,通过此id可以查询数据。

3.取消下载

downloadManager.remove(REFERENCE_1, REFERENCE_2, REFERENCE_3);
该方法返回成功取消的下载的个数,如果一个下载被取消了,所有相关联的文件,部分下载的文件和完全下载的文件都会被删除.

4.Request类的介绍

/**
* 方法1:
* 目录: Android -> data -> com.app -> files -> Download -> dxtj.apk
* 这个文件是你的应用所专用的,软件卸载后,下载的文件将随着卸载全部被删除
*/
request.setDestinationInExternalFilesDir( this , Environment.DIRECTORY_DOWNLOADS ,  "dxtj.apk" );
/**
* 方法2:
* 下载的文件存放地址  SD卡 download文件夹,dxtj.apk
* 软件卸载后,下载的文件会保留
*/
//在SD卡上创建一个文件夹
request.setDestinationInExternalPublicDir(  "/epmyg/"  , "dxtj.apk" ) ;
/**
* 方法3:
* 如果下载的文件希望被其他的应用共享
* 特别是那些你下载下来希望被Media Scanner扫描到的文件(比如音乐文件)
*/
request.setDestinationInExternalPublicDir( Environment.DIRECTORY_MUSIC,  "告白气球.mp3" );
/**
* 方法4【投资界和新芽目前用这个】
* 文件将存放在外部存储的确实download文件内,如果无此文件夹,创建之,如果有,下面将返回false。
* 系统有个下载文件夹,比如小米手机系统下载文件夹  SD卡--> Download文件夹
*/
//创建目录
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ;
//设置文件存放路径
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS  , "dxtj.apk" ) ;
  • 4.1 指定下载的类型
//指定在WIFI状态下,执行下载操作。
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
//指定在MOBILE状态下,执行下载操作
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE);
//是否允许漫游状态下,执行下载操作
request.setAllowedOverRoaming(boolean);
//是否允许“计量式的网络连接”执行下载操作
request.setAllowedOverMetered(boolean); //默认是允许的。
  • 4.2 定制Notification样式
//设置Notification的标题和描述
request.setTitle("标题"); 
request.setDescription("描述");
//设置Notification的显示,和隐藏。
request.setNotificationVisibility(visibility);

VISIBILTY_HIDDEN: Notification:将不会显示,如果设置该属性的话,必须要添加权限
android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
VISIBILITY_VISIBLE: Notification显示,但是只是在下载任务执行的过程中显示,下载完成自动消失。(默认值)
VISIBILITY_VISIBLE_NOTIFY_COMPLETED : Notification显示,下载进行时,和完成之后都会显示。
VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION :只有当任务完成时,Notification才会显示。
对Notification的设定方法相对较少。
  • 4.3 设置下载文件类型
request.setMimeType("application/vnd.android.package-archive");

这是安卓.apk文件的类型。有些机型必须设置此方法,才能在下载完成后,点击通知栏的Notification时,才能正确的打开安装界面。不然会弹出一个Toast(can not open file).其他文件类型的MimeType
  • 4.4 添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等
request.addRequestHeader(String header, String value);

5.Query 类

  • 我们的需求,可能不只是在Notification 中显示进度就好了,也许,在app中也需要获取实时下载进度。所以Query类就是提供查询的一些方法。
  • 但API中就只有两个方法,原来,他把数据保存在数据库中去了。我们需要获得一个Cursor 结果集,通过结果集获得我们想要的数据。
DownloadManager.Query query = new DownloadManager.Query();
Cursor cursor = downloadManager.query(query.setFilterById(id));
if (cursor != null && cursor.moveToFirst()) {
    //下载的文件到本地的目录
    String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
    //已经下载的字节数
    int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
    //总需下载的字节数
    int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
    //Notification 标题
    String title =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
    //描述
    String description =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION));
    //下载对应id
    long id =cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
    //下载文件名称
    String filename =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
    //下载文件的URL链接
    String url =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI));
}

6.不足之处

  • 1、我发现,在下载的时候,发送Notification时 是没有声音的。也没有设置声音的方法。不过这影响不大。主要的功能实现就好。
  • 2、因为这是系统的类,每个系统的Notification界面是不一样的。这就是每个rom厂家的自定义了。小米和魅族的就大不一样。魅族Notification上有一个下载暂停的按钮,而小米没有。所以导致Notification是不能统一的。其实,暂停的话用户可以点击notification,进入到下载管理界面,就有暂停按钮了。

7.代码案例【简易】

/**
* 开始下载
* @param context
*/
private static void startDownload(Context context) {
    int status = getDownloadStatus(context);
    if (status != DOWNLOAD_STATUS_NEED_LOAD) {
        // 不用下载则无需下列操作
        return;
    }

    DialogUtils.showToast(context, "开始下载安装包...");
    // parse url
    Uri mUri = Uri.parse(path);
    // create request
    DownloadManager.Request r = new DownloadManager.Request(mUri);
    // set request property
    String apkName = getDownloadApkName(context);
    // set 下载路径
    r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName);
    // set Notification
    r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    // create manager
    DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    // key code, set mine type
    r.setMimeType("application/vnd.android.package-archive");
    // add to queue
    dm.enqueue(r);
}

/**获得Apk的名称*/
@NonNull
private static String getDownloadApkName(Context context) {
    return context.getString(R.string.app_name) + "_" + version + ".apk";
}

/**
* 判断当前版本文件下载状态
* @param context
* @return
*/
private static int getDownloadStatus(Context context) {
    DownloadManager.Query query = new DownloadManager.Query();
    DownloadManager dm = (DownloadManager) context.getSystemService(Activity.DOWNLOAD_SERVICE);
    Cursor c = dm.query(query);

    if (!c.moveToFirst()) {
        // 无下载内容
        return DOWNLOAD_STATUS_NEED_LOAD;
    }

    do {
        int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
        String title = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE));
        String apkName = getDownloadApkName(context);
        if (title.equals(apkName)) {
            // 如果下载列表中文件是当前版本应用,则继续判断下载状态
            if (status == DownloadManager.STATUS_SUCCESSFUL) {
                // 如果已经下载,返回状态,同时直接提示安装
                String uri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                promptInstall(context, Uri.parse(uri));
                return DOWNLOAD_STATUS_LOADED;
            } else if (status == DownloadManager.STATUS_RUNNING
                    || status == DownloadManager.STATUS_PAUSED
                    || status == DownloadManager.STATUS_PENDING) {
                return DOWNLOAD_STATUS_RUNNING;
            } else {
                // 失败也视为可以再次下载
                return DOWNLOAD_STATUS_NEED_LOAD;
            }
        }
    } while (c.moveToNext());
    return DOWNLOAD_STATUS_NEED_LOAD;
}

/**
* 根据下载队列id获取下载Uri
* @param enqueueId
* @return null-获取不到
*/
public static Uri getDownloadUriById(Context context, long enqueueId) {
    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterById(enqueueId);
    DownloadManager dm = (DownloadManager) context.getSystemService(Activity.DOWNLOAD_SERVICE);
    Cursor c = dm.query(query);
    if (c.moveToFirst()) {
        int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
        if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
            String uri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
            return Uri.parse(uri);
        }
    }
    return null;
}

/**
* 安装文件
* @param context      上下文
* @param data          uri地址
*/
public static void promptInstall(Context context, Uri data) {
    Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(data, "application/vnd.android.package-archive");
    // FLAG_ACTIVITY_NEW_TASK 可以保证安装成功时可以正常打开 app
    promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(promptInstall);
}

其他
知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
领英:https://www.linkedin.com/in/chong-yang-049216146/
简书:http://www.jianshu.com/u/b7b2c6ed9284
csdn:http://my.csdn.net/m0_37700275
网易博客:http://yangchong211.blog.163.com/
新浪博客:http://blog.sina.com.cn/786041010yc
github:https://github.com/yangchong211
喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
脉脉:yc930211
开源中国:https://my.oschina.net/zbj1618/blog
邮箱:yangchong211@163.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • 从Android 2.3(API level 9)开始Android用系统服务(Service)的方式提供了Dow...
    柨柨阅读 2,680评论 1 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 选自《成人庄子实录》。愿我今后,不仅仅分享一些喜欢的“资讯”,而能把时间多用在现实层面,落实在做事情中。因为,最能...
    我的名字叫流浪阅读 795评论 3 2
  • 还记得小美大学的时候放假去外面玩,前期各种百度搜索攻略,之后准备 7 天的行程计划 excel,word,ps 都...
    最美应用阅读 359评论 0 1