• 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

  • 相关阅读:
    两个jquery编写插件实例
    jquery编写插件(转)
    前后端分离
    理解流式布局
    元素外边距溢出(塌陷)
    超级有用的9个PHP代码片段
    php实现redis锁机制
    php程序守护进程
    SESSION机制
    php面试
  • 原文地址:https://www.cnblogs.com/wlrhnh/p/4641105.html
Copyright © 2020-2023  润新知