DownloadManager补漏

原始完成于:2014-10-24 20:01:03

DownloadManager是一个处理HTTP下载请求的系统服务:

1. 基本用法

1     private void download() {
2         Request request = new Request(Uri.parse("http://files.cnblogs.com/wlrhnh/JeejenKnowledge-15793-release.apk"));
3         request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "4444.zip");
4         request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
5         long id = mDownloadMgr.enqueue(request);
6         Log.d("MMLL", "下载 id = " + id);
7     }

绿色代码指定文件下载路径,如果不指定,那么默认下载在“/data/data/com.android.providers.downloads/cache/***.apk”。代码中的id是个非常重要的属性,它表示DownloadManager给这次下载指定的ID,同时也是该文件在数据库中的column_id。默认行为,下载完成后Notification会自动消失;

红色代码表示下载完成后,Notification仍旧显示在下拉列表中,该API只能在API 11之后调用。

2. 获取下载文件的信息

API 11中新增了一个API,可以直接去到下载文件的Uri,如下:

1 Uri uri = mDownloadMgr.getUriForDownloadedFile(_id);
2 uri = file:///storage/emulated/0/Download/4444-4.zip //指定路径
3 uri = content://downloads/my_downloads/118 //默认路径

代码中红色log指的分别是指定路径和默认路径下得到的Uri,但是在API 低于11的系统中怎么使用呢?

DownloadManager提供了Query接口,代码如下:

 1     int _id = -1;
 2     String local_filename = null;
 3     String local_uri = null;
 4     Query query = new Query();
 5     Cursor cursor = mDownloadMgr.query(query);
 6     if (cursor != null) {
 7         cursor.moveToFirst();
 8         _id = cursor.getInt(cursor.getColumnIndex("_id"));
 9         local_filename = cursor.getString(cursor.getColumnIndex("local_filename"));
10         local_uri = cursor.getString(cursor.getColumnIndex("local_uri"));
11     }

上面代码中得到的Cursor的数量为1,且就是自己应用程序中调用DownloadManager下载的最新一次文件信息, 可以看到query既没有设置Uri,也没有指定_id,那怎么返回这唯一的一条信息呢?其实在生成query时有一些默认值,其中Uri如下:

1 /**
2 * The content:// URI to access downloads owned by the caller‘s UID.
3 */
4 public static final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");

而且秘密就在注释中,查询时会更据caller的UID,所以才会返回自己下载的文件的信息;

cursor的全部Column如下:

 1 D/MMLL    (16614): column = _id
 2 D/MMLL    (16614): column = local_filename
 3 D/MMLL    (16614): column = mediaprovider_uri
 4 D/MMLL    (16614): column = destination
 5 D/MMLL    (16614): column = title
 6 D/MMLL    (16614): column = description
 7 D/MMLL    (16614): column = uri
 8 D/MMLL    (16614): column = status
 9 D/MMLL    (16614): column = hint
10 D/MMLL    (16614): column = media_type
11 D/MMLL    (16614): column = total_size
12 D/MMLL    (16614): column = last_modified_timestamp
13 D/MMLL    (16614): column = bytes_so_far
14 D/MMLL    (16614): column = local_uri
15 D/MMLL    (16614): column = reason
16 D/MMLL    (16614): column = bypass_recommended_size_limit
17 D/MMLL    (16614): column = allowed_network_types
18 D/MMLL    (16614): column = entity

对应的值如下:

 1 D/MMLL    (22053): cursor.getString(0) = 107
 2 D/MMLL    (22053): cursor.getString(0) = /data/data/com.android.providers.downloads/cache/4_lauchmode.zip
 3 D/MMLL    (22053): cursor.getString(0) = null
 4 D/MMLL    (22053): cursor.getString(0) = 2
 5 D/MMLL    (22053): cursor.getString(0) = 4_lauchmode.zip
 6 D/MMLL    (22053): cursor.getString(0) =
 7 D/MMLL    (22053): cursor.getString(0) = http://files.cnblogs.com/wlrhnh/4_lauchmode.zip
 8 D/MMLL    (22053): cursor.getString(0) = 200
 9 D/MMLL    (22053): cursor.getString(0) = null
10 D/MMLL    (22053): cursor.getString(0) = application/zip
11 D/MMLL    (22053): cursor.getString(0) = 1315681
12 D/MMLL    (22053): cursor.getString(0) = 1414137592255
13 D/MMLL    (22053): cursor.getString(0) = 1315681
14 D/MMLL    (22053): cursor.getString(0) = content://downloads/my_downloads/107
15 D/MMLL    (22053): cursor.getString(0) = placeholder
16 D/MMLL    (22053): cursor.getString(0) = 0
17 D/MMLL    (22053): cursor.getString(0) = -1
18 D/MMLL    (22053): cursor.getString(0) = null

或者

 1 D/MMLL    ( 8796): cursor.getString(0) = 113
 2 D/MMLL    ( 8796): cursor.getString(0) = /storage/emulated/0/Download/4444.zip
 3 D/MMLL    ( 8796): cursor.getString(0) = content://media/external/file/34731
 4 D/MMLL    ( 8796): cursor.getString(0) = 4
 5 D/MMLL    ( 8796): cursor.getString(0) = 4444.zip
 6 D/MMLL    ( 8796): cursor.getString(0) =
 7 D/MMLL    ( 8796): cursor.getString(0) = http://files.cnblogs.com/wlrhnh/4_lauchmode.zip
 8 D/MMLL    ( 8796): cursor.getString(0) = 200
 9 D/MMLL    ( 8796): cursor.getString(0) = file:///storage/emulated/0/Download/4444.zip
10 D/MMLL    ( 8796): cursor.getString(0) = application/zip
11 D/MMLL    ( 8796): cursor.getString(0) = 1315681
12 D/MMLL    ( 8796): cursor.getString(0) = 1414149416875
13 D/MMLL    ( 8796): cursor.getString(0) = 1315681
14 D/MMLL    ( 8796): cursor.getString(0) = file:///storage/emulated/0/Download/4444.zip
15 D/MMLL    ( 8796): cursor.getString(0) = placeholder
16 D/MMLL    ( 8796): cursor.getString(0) = 0
17 D/MMLL    ( 8796): cursor.getString(0) = -1
18 D/MMLL    ( 8796): cursor.getString(0) = null

3. 监听下载状态的BroadCast,代码如下:

 1     private void regisiterBroadCast() {
 2         IntentFilter intentFilter = new IntentFilter();
 3         intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
 4         intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);
 5         intentFilter.addAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
 6
 7         this.registerReceiver(new BroadcastReceiver() {
 8             @Override
 9             public void onReceive(Context context, Intent intent) {
10                 if (intent != null) {
11                     Log.d("MMLL", "intent.getAction = " + intent.getAction());
12                     Bundle bundle = intent.getExtras();
13                     if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(intent.getAction())) {
14                         Log.d("MMLL", "ACTION_DOWNLOAD_COMPLETE");
15                     } else if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(intent.getAction())) {
16                         Log.d("MMLL", "ACTION_NOTIFICATION_CLICKED");
17                     } else if (DownloadManager.ACTION_VIEW_DOWNLOADS.equals(intent.getAction())) {
18                         Log.d("MMLL", "ACTION_VIEW_DOWNLOADS");
19                     }
20                 }
21             }
22         }, intentFilter);
23     }

除了下载完成后发送的广播能接受到之外,其他的两个广播暂时还收不到,待研究;

4. 如果下载的是APK,并且药调用Installer来安装,安装的代码如下:

1     private void installApk(Uri uri) {
2         Intent intent = new Intent(Intent.ACTION_VIEW);
3         intent.setDataAndType(uri, "application/vnd.android.package-archive");
4         startActivity(intent);
5     }

可见需要传入一个Uri,在前面的分析中,我们知道设置下载路径和默认下载路径返回的Uri是不一样的,

1 uri = file:///storage/emulated/0/Download/4444-4.zip //指定路径
2 uri = content://downloads/my_downloads/118 //默认路径

此时第一个uri传进去是可以正常安装的,但是第二个uri则不行,会提示“包解析失败”神马的,因此,如果下载的是APK,那么最好还是指定一下下载路径;

5. 那么在API 11之前如何获取Uri呢?

有两种方式:

1>. 前面提到的查询数据库,字段是local_uri

2>. 有一种比较复杂的方式,仅供参考:

 1     public Uri getDownloadFile() {
 2         long downloadId = getDownloadId();
 3         int status = queryDownloadStatus(downloadId);
 4         if (DownloadManager.STATUS_SUCCESSFUL == status) {
 5             if (Build.VERSION.SDK_INT >= 11) {
 6                 return mDownloadMgr.getUriForDownloadedFile(downloadId);
 7             } else {
 8                 FileOutputStream outStream = null;
 9                 ParcelFileDescriptor.AutoCloseInputStream instream = null;
10                 try {
11                     ParcelFileDescriptor desc = mDownloadMgr.openDownloadedFile(downloadId);
12                     instream = new ParcelFileDescriptor.AutoCloseInputStream(desc);
13
14                     String fileName = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
15                     fileName += "/jeejen/" + Calendar.getInstance().getTimeInMillis() + "_" + downloadId + ".apk";
16                     File file = new File(fileName);
17                     if (!file.getParentFile().exists()) {
18                         file.getParentFile().mkdirs();
19                     }
20                     file.createNewFile();
21                     outStream = new FileOutputStream(file);
22                     byte[] buffer = new byte[1024];
23                     int read;
24
25                     while ((read = instream.read(buffer)) != -1) {
26                         outStream.write(buffer, 0, read);
27                     }
28
29                     return Uri.fromFile(file);
30                 } catch (Exception e) {
31                     e.printStackTrace();
32                     return null;
33                 } finally {
34                     try {
35                         if (outStream != null) {
36                             outStream.close();
37                         }
38                     } catch (Exception e) {
39                     }
40                     try {
41                         if (instream != null) {
42                             instream.close();
43                         }
44                     } catch (Exception e) {
45                     }
46                 }
47             }
48         }
49         return null;
50     }

个人觉得这种方式比较复杂,但不失为一种思路。比较简单粗暴的方式是:

 1 private void copyToSD(String path) {
 2         FileInputStream inputStream = null;
 3         FileOutputStream outputStream = null;
 4         try {
 5             inputStream = new FileInputStream(path);
 6             outputStream = new FileOutputStream("sdcard/jeejen/大卫.zip");
 7             byte[] bb = new byte[1024];
 8             int count = -1;
 9             while ((count = inputStream.read(bb)) != -1) {
10                 outputStream.write(bb, 0, count);
11             }
12             outputStream.flush();
13         } catch (FileNotFoundException e) {
14             e.printStackTrace();
15         } catch (IOException e) {
16             e.printStackTrace();
17         } finally {
18             if (inputStream != null) {
19                 try {
20                     inputStream.close();
21                 } catch (IOException e) {
22                     e.printStackTrace();
23                 }
24             }
25             if (outputStream != null) {
26                 try {
27                     outputStream.close();
28                 } catch (IOException e) {
29                     e.printStackTrace();
30                 }
31             }
32         }
33     }

把local_filename字段对应的值传进去即可;

6. 如何获取当前下载进度?

获取进度的基本原理是注册一个ContentObserver,因为伴随着下载,会往数据库中写入下载文件的大小和当前下载完成的大小。代码如下:

 1     private void getProgress(long id) {
 2         long total_size = -1L;
 3         long bytes_so_far = -1L;
 4         Query query = new Query().setFilterById(id);
 5         Cursor cur = mDownloadMgr.query(query);
 6         if (cur != null && cur.moveToFirst()) {
 7             do {
 8                 total_size = cur.getLong(cur.getColumnIndex("total_size"));
 9                 bytes_so_far = cur.getLong(cur.getColumnIndex("bytes_so_far"));
10                 Log.d("MMLL", "bytes_so_far = " + bytes_so_far + ", total_size = " + total_size);
11             } while (cur.moveToNext());
12         }
13     }
14
15     private class DownloadChangeObserver extends ContentObserver {
16
17         public DownloadChangeObserver(Handler handler) {
18             super(handler);
19         }
20
21         @Override
22         public void onChange(boolean selfChange) {
23             super.onChange(selfChange);
24             getProgress(id);
25         }
26
27         /*@Override
28         public void onChange(boolean selfChange, Uri uri) {
29             super.onChange(selfChange, uri);
30         }*/
31
32     }

然后注册,如下:

1 getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true, new DownloadChangeObserver(null));

OK,至此Over

时间: 2024-10-06 23:12:33

DownloadManager补漏的相关文章

DownloadManager 下载管理类

演示 简介 从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到.下载管理类可以长期处理多个HTTP下载任务,客户端只需要给出请求的Uri和存放目标文件的位置即可,下载管理使用了一个AIDL服务器,所以可以放心的在后台执行,同时实例化的方法需要使用getSystemService(Context.DOWNLOAD_SERVICE) ,我们可以轻松的通过新增的这个API实现Android平台上的文件下载操作. Do

android webview downloadManager文件下载管理

一.downloadmanager类说明: 从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到.下载管理类可以长期处理多个HTTP下载任务,客户端只需要给出请求的Uri和存放目标文件的位置即可,下载管理使用了一个AIDL服务器所以可以放心的在后台执行,同时实例化的方法需要使用getSystemService(Context.DOWNLOAD_SERVICE) ,Android123再次提醒使用API Level

使用DownloadManager实现下载功能

使用Android自带的DownloadManager实现下载功能 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(APK_URL)); request.setDestinationInExternalPublicDir(DOWNLOAD_FOLDER_NAME, DOWNLOAD_FILE_NAME); request.setTitle(getString(R.string.download_not

Java补漏(二)

 第三章Java基本语法 3.1 标准输入输出流 3.1.1 标准输出输入流 System.in:标准输入流 System.out:标准输出流 System.err:标准错误输出流 3.1.2 标准输出流重定位 一般的输出会默认在命令行模式.终端机输出,可是在执行程序时使用将输出结果定位至指定的文件即可. java HelloJava > HelloJavaResult.txt 3.2 基本数据类型 整形{short(2个字节).int(4个字节).long(8个字节)} 字节型{byte(

6.18 函数查缺,补漏

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace 函数查缺补漏 8 { 9 10 class Program 11 { 12 //1. 没有返回值没有参数 13 public void aa() 14 { 15 Console.WriteLine("查缺补漏"

使用downloadmanager调用系统的下载

/** * 文件名 UpdateDownload.java * 包含类名列表 com.issmobile.numlibrary.tool * 版本信息  版本号  * 创建日期 2014年7月14日 * 版权声明  */ package com.issmobile.numlibrary.tool; import com.iss.utils.LogUtil; import android.annotation.SuppressLint;import android.app.DownloadMana

Android中的文件下载——DownLoadManager

一.问题概述 在android开发中,经常会使用到文件下载的功能,比如app版本更新等.在api level 9之后,android系统为我们提供了DownLoadManager类,这是android提供的系统服务,我们通过这个服务完成文件下载.整个下载过程全部交给系统负责,不需要我们过多的处理. 通过API文档,可以看出DownLoadManager包含两个内部类: DownLoadManager.Query:主要用于查询下载信息. DownLoadManager.Request:主要用于发起

Android 下载模块分析(DownloadManager和DownloadProvider)

Android下载模块主要有2个部分组成:DownloadManager和DownloadProvider:其中DownloadManager提供接口供调用,具体的实现是 DownloadProvider,包括相关数据信息的保存及文件下载. DownloadManager是系统开放给第三方应用使用的类,包含两个静态内部类DownloadManager.Query和DownloadManager.Request. DownloadManager.Request用来请求一个下载 DownloadMa

Android系统下载管理DownloadManager功能介绍及使用示例

http://www.trinea.cn/android/android-downloadmanager/ 本文主要结合源码介绍Android系统下载管理DownloadManager的强大功能及使用.这是许久来准备写的一系列博客,这篇主要介绍DownloadManager的功能和示例,后面还有两篇会介绍下载管理的底层设计(DownloadProvider.DownloadManager.DownloadManagerUI).下载管理如何进行功能增强和bug修改. 示例APK可从这些地址下载:G