Android系统下载管理DownloadManager

转载 2017年01月03日 14:42:37

一. DownloadManager简单介绍

DownloadManger是android 2.3(api level 9)开始 提供的用于优化处理长时间的下载操作。DownloadManager 处理Http/Https连接并监控连接中的状态变化及系统重启来确保每一个下载任务顺利完成。大多数涉及到下载的情况中使用DownloadManager都是很好的选择,尤其是后台继续下载,下载状态回调,断点续传,下载环境设置,下载文件的操作等方面,支持的很好。

DownloadManager是系统开放给第三方应用使用的类,包含两个静态内部类DownloadManager.Query和DownloadManager.Request。DownloadManager.Request用来请求一个下载,DownloadManager.Query用来查询下载信息,具体接口信息可参看最后的api说明。

二. DownloadManager使用

DownloadManager主要对外提供了以下接口:

  • public long enqueue(Request request)执行下载,返回downloadId,downloadId可用于后面查询下载信息。若网络不满足条件、Sdcard挂载中、超过最大并发数等异常会等待下载,正常则直接下载。

  • int remove(long… ids) 删除下载,若下载中取消下载。会同时删除下载文件和记录。

  • Cursor query(Query query) 查询下载信息。

  • getMaxBytesOverMobile(Context context) 返回移动网络下载的最大值

  • rename(Context context, long id, String displayName) 重命名已下载项的名字

  • getRecommendedMaxBytesOverMobile(Context context) 获取建议的移动网络下载的大小

  • 其它:通过查看代码我们可以发现还有个CursorTranslator私有静态内部类。这个类主要对Query做了一层代理。将DownloadProvider和DownloadManager之间做个映射。将DownloadProvider中的十几种状态对应到了DownloadManager中的五种状态,DownloadProvider中的失败、暂停原因转换为了DownloadManager的原因。

三. DownloadManager下载示例

1.下载所需权限

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

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

2.调用DownloadManager.Request开始下载

DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);

String apkUrl = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";

DownloadManager.Request request = new 

DownloadManager.Request(Uri.parse(apkUrl));

request.setDestinationInExternalPublicDir("dirType", "/mydownload/QQ.apk");

// request.setTitle("TX QQ");

// request.setDescription("This is TX QQ");

// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

//request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);

// 
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);

//request.setMimeType("application/cn.trinea.download.file"); 

long downloadId = downloadManager.enqueue(request);

上面代码是将一个下载url加入到系统下载队列,在条件满足时会自动开始下载,调用enqueue()返回的是一个long型的id值,该id为该下载项的唯一id。

可以对Request(下载项)进行属性设置,除了构造参数的URI必填,其他条件都是可选的,如下载的本地路径,下载后的重命名,下载通知的显示与不显示或者通知的样式,下载允许的网络类型,下载项的MimeType等。

request.setMimeType(“application/cn.trinea.download.file”);

设置下载文件的mineType。因为下载管理Ui中点击某个已下载完成文件及下载完成点击通知栏提示都会根据mimeType去打开文件,所以我们可以利用这个属性。比如上面设置了mimeType为application/cn.trinea.download.file,我们可以同时设置某个Activity的intent-filter为application/cn.trinea.download.file,用于响应点击的打开文件。

<intent-filter> 

<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />

<data android:mimeType="application/cn.trinea.download.file" />

</intent-filter>

添加请求下载的网络链接的http头,比如User-Agent,gzip压缩等:

request.addRequestHeader(String header, String value);

3.下载进度状态的监听及查询

DownloadManager下载工具并没有提供相应的回调接口用于返回实时的下载进度状态,但通过安卓的四大组件之一ContentProvider,我们可以监听到当前的下载项的进度状态变化。

downloadManager.getUriForDownloadedFile(id);

该方法会返回一个下载项的Uri,如:content://downloads/my_downloads/125
ContentObserver——内容观察者,可监听观察特定Uri指向的数据库项的变化,进而进行相应的处理,其中我们会监听Uri.parse(“content://downloads/my_downloads”)。然后查询下载状态和进度,进行下一步操作如发送handler进行更新UI。

下面的方法可根据下载项的id,查询下载项的下载进度状态并返回“当前已下载字节”、“总字节”、“当前下载状态”:

 public int[] getBytesAndStatus(long downloadId) {
    int[] bytesAndStatus = new int[] { -1, -1, 0 };
    DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
    Cursor c = null;
    try {
        c = downloadManager.query(query);
        if (c != null && c.moveToFirst()) {
            bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
            bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
            bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
        }
    } finally {
        if (c != null) {
            c.close();
        }
    }
    return bytesAndStatus;
}

从上面代码可以看出我们主要调用DownloadManager.Query()进行查询。DownloadManager.Query为下载管理对外开放的信息查询类,主要包括以下接口:

  • setFilterById(long… ids)根据下载id进行过滤
  • setFilterByStatus(int flags)根据下载状态进行过滤
  • setOnlyIncludeVisibleInDownloadsUi(boolean value)根据是否在download ui中可见进行过滤。
  • orderBy(String column, int direction)根据列进行排序,不过目前仅支持
  • DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP和
  • DownloadManager.COLUMN_TOTAL_SIZE_BYTES排序。

需要对ContentProvider提供的内容进行观察,需要重新ContentObserver中的 onChange(boolean selfChange)方法,并在activity/fragment的相应生命周期方法中去注册/注销Observer:

 class DownloadStatusObserver extends ContentObserver {
        public DownloadStatusObserver() {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            int[] bytesAndStatus = getBytesAndStatus(downloadId);
            int currentSize = bytesAndStatus[0];//当前大小
            int totalSize = bytesAndStatus[1];//总大小
            int status = bytesAndStatus[2];//下载状态

        }

    }

@Override
    protected void onResume() {
        super.onResume();
        getContentResolver().registerContentObserver(CONTENT_URI, true, observer);
}

 @Override
    protected void onDestroy() {
        super.onDestroy();
        getContentResolver().unregisterContentObserver(observer);
    }
  • 如果界面上过多元素需要更新,且网速较快不断的执行onChange会对页面性能有一定影响。推荐ScheduledExecutorService定期查询,如下:

      //三秒定时刷新一次
      public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
      Runnable command = new Runnable() {
    
              @Override
              public void run() {
                  updateView();
              }
          };
      scheduledExecutorService.scheduleAtFixedRate(command, 0, 3, TimeUnit.SECONDS);

4.下载成功监听

下载完成后,下载管理会发出DownloadManager.ACTION_DOWNLOAD_COMPLETE这个广播,并传递downloadId作为参数。通过接受广播我们可以打开对下载完成的内容进行操作。代码如下:

class CompleteReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // get complete download id
        long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        // to do here
    }
};

private CompleteReceiver       completeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.download_manager_demo);

    …
    completeReceiver = new CompleteReceiver();
    /** register download success broadcast **/
    registerReceiver(completeReceiver,
                     new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(completeReceiver);
}

5、响应通知栏点击

  • 响应下载中通知栏点击

点击下载中通知栏提示,系统会对下载的应用单独发送Action为DownloadManager.ACTION_NOTIFICATION_CLICKED广播。

intent.getData为content://downloads/all_downloads/29669,最后一位为downloadId。

如果同时下载多个应用,intent会包含DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS这个key,表示下载的的downloadId数组。

  • 响应下载完成通知栏点击

下载完后会调用下面代码进行处理,从中我们可以发现系统会调用View action根据mimeType去查询。所以可以利用我们在介绍的DownloadManager.Request的setMimeType函数。

private void openDownload(Context context, Cursor cursor) {
    String filename = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl._DATA));
    String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_MIME_TYPE));
    Uri path = Uri.parse(filename);
    // If there is no scheme, then it must be a file
    if (path.getScheme() == null) {
        path = Uri.fromFile(new File(filename));
    }
    Intent activityIntent = new Intent(Intent.ACTION_VIEW);
    mimetype = DownloadDrmHelper.getOriginalMimeType(context, filename, mimetype);
    activityIntent.setDataAndType(path, mimetype);
    activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    try {
        context.startActivity(activityIntent);
    } catch (ActivityNotFoundException ex) {
        Log.d(Constants.TAG, "no activity for " + mimetype, ex);
    }
}

6、通过隐式意图打开系统下载界面

Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
startActivity(intent);

四.DownloadManager/Request/Query 接口参考

DownloadManager:
 long enqueue(Request request)

将一个新的下载项加入队列,该下载项将在downloadmanager准备好执行该下载项/连接可用时自动开始下载,返回该下载的唯一id

int remove(long... ids)

移除一个或多个下载项,返回改变的个数

Cursor query(Query query)

执行一个query Qurey类下面有介绍

openDownloadedFile(long id)

打开一个文件(该文件必须已下载完成)

getUriForDownloadedFile(long id)

返回一个已下载项的Uri,如:content://downloads/my_downloads/103

getMimeTypeForDownloadedFile(long id)

返回一个已下载项mimetype

restartDownload(long... ids)

重新已完成的下载项(下载成功,下载失败)开始下载;

getMaxBytesOverMobile(Context context)

返回移动网络下载的最大值

rename(Context context, long id, String displayName)

重命名已下载项的名字

getRecommendedMaxBytesOverMobile(Context context)

获取建议的移动网络下载的大小

addCompletedDownload(String title, String description,boolean isMediaScannerScannable, String mimeType, String path, long length,boolean showNotification)

添加一个文件到系统的下载文件数据库中,并且可以在系统下载app中显示出该文件的下载条目;
根据api上的说明,该方法对于使一个文件成为被MediaScanner可扫描的文件很有用,调用该方法,可将指定的文件变成scannable by MediaScanner by setting the param isMediaScannerScannable to true.如相册gallary应用中就很有用


Request
setDestinationUri(Uri uri) :

自定义下载本地路径,传入一个Uri类型;

setDestinationInExternalFilesDir(Context context, String dirType,String subPath)  :

自定义下载本地路径,为内置sd卡的Android/data/package-name/ 父目录下,该目录下的内容会随应用的卸载而删除清空数据,dirType:文件的类型(Environment.DIRECTORY_DOWNLOADS,Environment.DIRECTORY_DCIM,......);
subPath:父目录下得子目录路径(/apk/myPath/myapk.apk)

setDestinationInExternalPublicDir(String dirType, String subPath) :

同上,下载到sd卡上共享的公共父目录下的subPath子目录下

allowScanningByMediaScanner() :

从字面意思可以明白,是允许该下载项可以被MediaScanner(多媒体文件的扫描工作)扫描到。(MediaScanner认识:http://blog.csdn.net/hellofeiya/article/details/8255898)

addRequestHeader(String header, String value) :

添加一个Http请求头信息到http头信息列表末尾

setTitle(CharSequence title)/setDescription(CharSequence description)

设置通知栏中下载通知的显示样式

setMimeType(String mimeType)

Set the MIME content type of this download. This will override the content type declared
 in the server's response.
设置MimeType,会覆盖服务器返回的已声明的content type

setShowRunningNotification(boolean show)(已过时)

设置开始下载时是否通过通知的形式显示该下载项状态,默认显示

setNotificationVisibility(int visibility)替代上面方法

visibility取值:
{@link #VISIBILITY_HIDDEN},
 {@link #VISIBILITY_VISIBLE},
 {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}.

setAllowedNetworkTypes(int flags)

设置下载时允许的网络环境,默认允许所有网络环境

setAllowedOverRoaming(boolean allow)

Set whether this download may proceed over a metered network
 connection. By default, metered networks are allowed.
设置是否允许漫游网络下的下载,默认允许漫游下载

setRequiresCharging(boolean requiresCharging)

设置是否必须充电状态下下载,默认否

setRequiresDeviceIdle(boolean requiresDeviceIdle)

设置是否必须手机处于空闲状态下载,默认否

setVisibleInDownloadsUi(boolean isVisible)

设置下载项是否在系统的 下载 app中显示 默认显示,打开系统自带下载app可查看该下载项


Query
setFilterById(long... ids)

根据id对downloadmanager中的下载项进行筛选,返回cursor

setFilterByStatus(int flags)

根据status对downloadmanager中的下载项进行筛选,返回cursor

setOnlyIncludeVisibleInDownloadsUi(boolean value)

设置为true,则是对仅仅显示在下载界面的下载项进行筛选,反之,未显示的也将被筛选

orderBy(String column, int direction)

对返回的Cursor所携带的数据根据column,direction进行排序
column取值 COLUMN_LAST_MODIFIED_TIMESTAMP
COLUMN_TOTAL_SIZE_BYTES
direction取值 ORDER_ASCENDING
ORDER_DESCENDING

具体参考DownloadManager官方API



文/jiaming_(简书作者)
原文链接:http://www.jianshu.com/p/7ad92b3d9069
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

Android 使用DownloadManager进行版本更新的完整方案

在Android App都会有版本更新的功能,以前我们公司是用友盟SDK更新功能,自己服务器没有这样的功能。版本检测、Apk下载都是使用友盟。最近看到友盟的版本更新SDK文档:十月份更新功能将会停止服...

Android系统内置下载器服务DownloadManager的使用

Android 程序开发中如果需要下载文件,除了自己程序内部实现下载外,还可以直接使用Android系统自带的下载器进行下载,使用系统下载器通常有两种方式,即浏览器直接下载和系统内置下载器Downlo...

Android中DownLoadManager的使用

项目本来开始使用的是友盟的自动提示更新功能,现在由于应用市场,系统厂商,运营商等多方面对友盟自动更新服务的限制,友盟将于2016年10月份停止所有应用的自动更新服务,这就让我倒霉了,得自己在客户端写自...

android app版本升级(DownloadManager、适配6.0、7.0)

说明: 1.本文使用系统DownloadManager在通知栏更新下载进度 2.动态权限使用第三方库EasyPermissions(https://github.com/googlesamp...

系统自带DownloadManager详解

前言:还在自己写downloadUtils吗?有了DownloadManger.你只需将UI画好,其他的事情,交给他来做就好了。简介:DownloadManager是android2.3以后,系统下载...

Android DownloadManager 的使用

从Android 2.3(API level 9)开始Android用系统服务(Service)的方式提供了Download Manager来优化处理长时间的下载操作。Download Manager...

安卓开发实战之app之版本更新升级(DownloadManager和http下载)完整实现

前言本文将讲解app的升级与更新。一般而言用户使用App的时候升级提醒有两种方式获得: 一种是通过应用市场 获取 一种是打开应用之后提醒用户更新升级 而更新操作一般是在用户点击了升级按钮之后开始执行的...

Android DownloadManager 的使用

从Android 2.3(API level 9)开始Android用系统服务(Service)的方式提供了Download Manager来优化处理长时间的下载操作。Download Manager...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

使用Android自带DownloadManager下载文件

SDK在API Level 9中加入了DownloadManager服务,可以将长时间的下载任务交给系统,完全由系统管理。 直接看实例代码: package com.hebaijun.do...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android系统下载管理DownloadManager
举报原因:
原因补充:

(最多只允许输入30个字)