原始完成于: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