DownLoadManager的下载及进度监听

  • 简介:
    DownLoadManager是一个系统服务,用于处理长时间下载任务,DownLoadManager会在后台处理HTTP交互和重试下载失败或跨连接启动系统,DownloadManager是用service更新进度,用广播监听下载完成和点击事件。DownLoadManager中有两个内部类:DownloadManager.Query和DownloadManager.Request。前者用于查询下载,后者用于设置一些效果设置。

  • DownloadManager.Request类的常用方法:

    addRequestHeader(String header, String value):添加一个请求头
    setAllowedNetworkTypes(int flags):设置下载时所使用的网络类型,提供的常量有:NETWORK_BLUETOOTH、NETWORK_MOBILE、NETWORK_WIFI。
    setAllowedOverRoaming(boolean allowed):是否允许漫游
    setDescription(CharSequence description):设置下载的描述信息
    setDestinationInExternalFilesDir(Context context, String dirType, String subPath):设置下载文件外部存储目录
    setDestinationUri(Uri uri):设置下载目标的URI
    setMimeType(String mimeType):设置MIME类型
    setShowRunningNotification(boolean show):设置是否显示下载进度提示
    setTitle(CharSequence title):设置下载时Notification的标题
    setVisibleInDownloadsUi(boolean isVisible):设置是否现在下载界面
    以上的各个方法,如果不设置的话就是默认的。

  • 有关暂停的一些状态,同样COLUMN_REASON字段的值定义常量有:

    int PAUSED_QUEUED_FOR_WIFI 由于移动网络数据问题,等待WiFi连接能用后再重新进入下载队列
    int PAUSED_UNKNOWN 未知原因导致了任务下载的暂停
    int PAUSED_WAITING_FOR_NETWORK 可能由于没有网络连接而无法下载,等待有可用的网络连接恢复
    int PAUSED_WAITING_TO_RETRY 由于重重原因导致下载暂停,等待重试

  • DownloadManager.Query类
      DownloadManager.Query类只有两个方法:setFilterById(long… ids)根据任务Id查找和setFilterByStatus(int flags) 根据任务状态查找。但是定义了很多常量:

    下载状态常量:

    int STATUS_FAILED 失败
    int STATUS_PAUSED 暂停
    int STATUS_PENDING 等待将开始
    int STATUS_RUNNING 正在处理中
    int STATUS_SUCCESSFUL 已经下载成功
    DownloadManager.Query类查询返回的是一个Cursor游标,上面描述的状态就会保存在COLUMN_STATUS 字段中。下载状态会以广播的形式通知,Android系统定义的Action有:
    ACTION_DOWNLOAD_COMPLETE下载完成的动作。
    ACTION_NOTIFICATION_CLICKED 当用户单击notification中下载管理的某项时触发。
    ACTION_VIEW_DOWNLOADS 查看下载项
    对于未完成的状态,我们查找COLUMN_REASON字段,定义的常量有:
    int ERROR_CANNOT_RESUME 不能够继续,由于一些其他原因。
    int ERROR_DEVICE_NOT_FOUND 外部存储设备没有找到,比如SD卡没有插入。
    int ERROR_FILE_ALREADY_EXISTS 要下载的文件已经存在了,要想重新下载需要删除原来的文件
    int ERROR_FILE_ERROR 可能由于SD卡原因导致了文件错误。
    int ERROR_HTTP_DATA_ERROR 在Http传输过程中出现了问题。
    int ERROR_INSUFFICIENT_SPACE 由于SD卡空间不足造成的
    int ERROR_TOO_MANY_REDIRECTS 这个Http有太多的重定向,导致无法正常下载
    int ERROR_UNHANDLED_HTTP_CODE 无法获取http出错的原因,比如说远程服务器没有响应。
    int ERROR_UNKNOWN 未知的错误类型.

  • 有关暂停的一些状态,同样COLUMN_REASON字段的值定义常量有:

    int PAUSED_QUEUED_FOR_WIFI 由于移动网络数据问题,等待WiFi连接能用后再重新进入下载队列
    int PAUSED_UNKNOWN 未知原因导致了任务下载的暂停
    int PAUSED_WAITING_FOR_NETWORK 可能由于没有网络连接而无法下载,等待有可用的网络连接恢复
    int PAUSED_WAITING_TO_RETRY 由于重重原因导致下载暂停,等待重试

  • DownLoadManager的下载及进度监听例子:
    service工具类如下:

public class DownloadServise extends Service {
   private static final String TAG = DownloadServise.class.getSimpleName();

   public static final int HANDLE_DOWNLOAD = 0x001;
   public static final String BUNDLE_KEY_DOWNLOAD_URL = "download_url";
   public static final float UNBIND_SERVICE = 2.0F;

   private Activity activity;
   private DownloadBinder binder;
   private DownloadManager downloadManager;
   private DownloadChangeObserver downloadObserver;
   private BroadcastReceiver downLoadBroadcast;
   private ScheduledExecutorService scheduledExecutorService;

   //下载任务ID
   private long downloadId;
   private String downloadUrl;
   public static OnProgressListener onProgressListener;

   @SuppressLint("HandlerLeak") public Handler downLoadHandler = new Handler() { //主线程的handler
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           if (onProgressListener != null && HANDLE_DOWNLOAD == msg.what) {
               //被除数可以为0,除数必须大于0
               if (msg.arg1 >= 0 && msg.arg2 > 0) {
                   onProgressListener.onProgress(msg.arg1 / (float) msg.arg2);
               }
           }
       }
   };

   private Runnable progressRunnable = new Runnable() {
       @Override
       public void run() {
           updateProgress();
       }
   };

   @Override
   public void onCreate() {
       super.onCreate();
       binder = new DownloadBinder();
   }

   @Override
   public IBinder onBind(Intent intent) {
       downloadUrl = intent.getStringExtra(BUNDLE_KEY_DOWNLOAD_URL);
       downloadApk(downloadUrl);
       return binder;
   }

   /**
    * 下载最新APK
    */
   private void downloadApk(String url) {
       downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
       downloadObserver = new DownloadChangeObserver();

       registerContentObserver();

       DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
       DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
       //request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "my.apk");
       request.setDestinationInExternalFilesDir(this, null, "my.apk");
       request.setTitle("title");
       //request.setDescription("下载中通知栏提示");
       request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
       request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
       //request.setMimeType("application/cn.trinea.download.file"); //用于响应点击的打开文件
       request.setVisibleInDownloadsUi(true);  //显示下载界面
       request.allowScanningByMediaScanner();  //准许被系统扫描到
       downloadId = downloadManager.enqueue(request);
       registerBroadcast(); //下载成功和点击通知栏动作监听
   }

   /**
    * 注册广播
    */
   private void registerBroadcast() {
       /**注册service 广播 1.任务完成时 2.进行中的任务被点击*/
       IntentFilter intentFilter = new IntentFilter();
       intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
       intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);
       registerReceiver(downLoadBroadcast = new DownLoadBroadcast(), intentFilter);
   }

   /**
    * 注销广播
    */
   private void unregisterBroadcast() {
       if (downLoadBroadcast != null) {
           unregisterReceiver(downLoadBroadcast);
           downLoadBroadcast = null;
       }
   }

   /**
    * 注册ContentObserver
    */
   private void registerContentObserver() {
       /** observer download change **/
       if (downloadObserver != null) {
           getContentResolver().registerContentObserver(
               Uri.parse("content://downloads/my_downloads"), false, downloadObserver);
       }
   }

   /**
    * 注销ContentObserver
    */
   private void unregisterContentObserver() {
       if (downloadObserver != null) {
           getContentResolver().unregisterContentObserver(downloadObserver);
       }
   }

   /**
    * 关闭定时器,线程等操作
    */
   private void close() {
       if (scheduledExecutorService != null && !scheduledExecutorService.isShutdown()) {
           scheduledExecutorService.shutdown();
       }

       if (downLoadHandler != null) {
           downLoadHandler.removeCallbacksAndMessages(null);
       }
   }

   /**
    * 发送Handler消息更新进度和状态
    * 将查询结果从子线程中发往主线程(handler方式),以防止ANR
    */
   private void updateProgress() {
       int[] bytesAndStatus = getBytesAndStatus(downloadId);
       downLoadHandler.sendMessage(downLoadHandler.obtainMessage(HANDLE_DOWNLOAD, bytesAndStatus[0], bytesAndStatus[1], bytesAndStatus[2]));
   }

   /**
    * 通过query查询下载状态,包括已下载数据大小,总大小,下载状态
    *
    * @param downloadId
    * @return
    */
   private int[] getBytesAndStatus(long downloadId) {
       int[] bytesAndStatus = new int[]{
               -1, -1, 0
       };
       DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
       Cursor cursor = null;
       try {
           cursor = downloadManager.query(query);
           if (cursor != null && cursor.moveToFirst()) {
               //已经下载文件大小
               bytesAndStatus[0] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
               //下载文件的总大小
               bytesAndStatus[1] = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
               //下载状态
               bytesAndStatus[2] = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
           }
       } finally {
           if (cursor != null) {
               cursor.close();
           }
       }
       return bytesAndStatus;
   }

   /**
    * 绑定此DownloadService的Activity实例
    *
    * @param activity
    */
   public void setTargetActivity(Activity activity) {
       this.activity = activity;
   }

   /**
    * 接受下载完成广播
    */
   private class DownLoadBroadcast extends BroadcastReceiver {

       @Override
       public void onReceive(Context context, Intent intent) {
           long downId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
           switch (intent.getAction()) {
               case DownloadManager.ACTION_DOWNLOAD_COMPLETE:
                   if (downloadId == downId && downId != -1 && downloadManager != null) {
                       Uri downIdUri = downloadManager.getUriForDownloadedFile(downloadId);

                       close();

                       if (downIdUri != null) {
                           SPTools.put(Constant.SP_DOWNLOAD_PATH, downIdUri.getPath());
                           LogUtil.showLog(TAG, "广播监听下载完成,APK存储路径为 :" + downIdUri.getPath());
                           installApk(context, downIdUri);
                       }
                       if (onProgressListener != null) {
                           onProgressListener.onProgress(UNBIND_SERVICE);
                       }
                   }
                   break;

               case DownloadManager.ACTION_NOTIFICATION_CLICKED:
                   ToastUitl.showToast("我被点击啦!");
                   break;
           }
       }
   }

   /**
    * 监听下载进度
    */
   private class DownloadChangeObserver extends ContentObserver {

       public DownloadChangeObserver() {
           super(downLoadHandler);
           scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
       }

       /**
        * 当所监听的Uri发生改变时,就会回调此方法
        * @param selfChange 此值意义不大, 一般情况下该回调值false
        */
       @Override
       public void onChange(boolean selfChange) {
           scheduledExecutorService.scheduleAtFixedRate(progressRunnable, 0, 1, TimeUnit.SECONDS); //在子线程中查询
       }
   }

   public class DownloadBinder extends Binder {
       /**
        * 返回当前服务的实例
        * @return
        */
       public DownloadServise getService() {
           return DownloadServise.this;
       }

   }

   public interface OnProgressListener {
       /**
        * 下载进度
        * @param fraction 已下载/总大小
        */
       void onProgress(float fraction);
   }

   /**
    * 对外开发的方法
    * @param onProgressListener
    */
   public void setOnProgressListener(OnProgressListener onProgressListener) {
       DownloadServise.onProgressListener = onProgressListener;
   }

   public static void installApk(Context context, Uri apkPath) {
       Intent intent = new Intent();
       intent.setAction(Intent.ACTION_VIEW);
       intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       intent.setDataAndType(apkPath, "application/vnd.android.package-archive");
       context.startActivity(intent);
   }

   @Override
   public void onDestroy() {
       super.onDestroy();
       unregisterBroadcast();
       unregisterContentObserver();
   }
}

绑定服务开始下载代码:

private ProgressDialog progress;
 private boolean isBindService;
 private ServiceConnection conn = new ServiceConnection() { //通过ServiceConnection间接可以拿到某项服务对象

   @Override
   public void onServiceConnected(ComponentName name, IBinder service) {
     DownloadServise.DownloadBinder binder = (DownloadServise.DownloadBinder) service;
     DownloadServise downloadServise = binder.getService();

     //接口回调,下载进度
     downloadServise.setOnProgressListener(new DownloadServise.OnProgressListener() {
       @Override
       public void onProgress(float fraction) {
         progress.setProgress((int)(fraction * 100));

         //判断是否真的下载完成进行安装了,以及是否注册绑定过服务
         if (fraction == downloadServise.UNBIND_SERVICE && isBindService) {
           progress.setProgress(100);
           progress.dismiss();
           unbindService(conn);
           isBindService = false;
         }
       }
     });
   }

   @Override
   public void onServiceDisconnected(ComponentName name) {

   }
 };

 private void initProgressBar() {
   progress = new ProgressDialog(this);
   progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
   progress.setProgress(1);
   progress.setCancelable(false);
   progress.setCanceledOnTouchOutside(false);
   progress.show();
 }

private void downLoadCs() {  //下载调用
   initProgressBar();
   removeOldApk();
   Intent intent = new Intent(this, DownloadServise.class);
   intent.putExtra(DownloadServise.BUNDLE_KEY_DOWNLOAD_URL, "http://imtt.dd.qq.com/16891/3804D823FD7A0710F664D0126841BD10.apk?fsname=com.cxtx.chefu.app_1.3.0_39.apk&csr=1bbd");
   isBindService = bindService(intent, conn, BIND_AUTO_CREATE); //绑定服务即开始下载 调用onBind()
 }

 /**
  * 删除上次更新存储在本地的apk
  */
 private void removeOldApk() {
   //获取老APK的存储路径
   File fileName = new File(SPTools.getString(Constant.SP_DOWNLOAD_PATH, ""));

   if (fileName != null && fileName.exists() && fileName.isFile()) {
     fileName.delete();
   }
 }

最后别忘了在AndroidManifest.xml中声明:

<service android:name=".service.DownloadServise"/>

其他参考:
网址1:http://www.jianshu.com/p/bb4cde6e88c6
网址2:http://blog.csdn.net/a907763895/article/details/12753149
Demo1: https://github.com/GitPhoenix/DownloadManager

.

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 从Android 2.3(API level 9)开始Android用系统服务(Service)的方式提供了Dow...
    柨柨阅读 2,698评论 1 4
  • DownloadManager实现下载、安装更新app功能 一款app必不可少的功能,就是自动检测版本实现下载、安...
    一点愁阅读 1,634评论 0 5
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,639评论 2 7
  • 眼看着2016年的日历就要翻完,新春佳节也近在咫尺,一年一度的同学聚会又在几个热心的同学张罗之下,紧锣密鼓地筹备之...
    苏城之歌阅读 2,449评论 36 31