码迷,mamicode.com
首页 > 其他好文 > 详细

DownloadManager 下载管理类

时间:2016-06-05 23:27:09      阅读:808      评论:0      收藏:0      [点我收藏+]

标签:


演示
技术分享
技术分享
技术分享 技术分享
技术分享


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

DownloadManager类提供了以下几种方法来处理,
long  enqueue(DownloadManager.Request request)   //存入队列一个新的下载项
ParcelFileDescriptor  openDownloadedFile(long id)  //打开一个下载后的文件用于读取,参数中的long型id是一个provider中的一条记录
Cursor  query(DownloadManager.Query query)  //查询一个下载,返回一个Cursor
int  remove(long... ids)  //取消下载,同时从下载管理中移除这些条
 技术分享
我们可以看到提供的方法都比较简单,给我们操作的最终封装成为一个provider数据库的方式进行添加、查询和移除,但是对于查询和添加任务的细节,我们要看看DownloadManager.Request类和DownloadManager.Query 类了。

DownloadManager.Request类
addRequestHeader(String header, String value)  // 添加一个Http请求报头,比如说User-Agent值可以为Android123或Windows XP等等了,主要是给服务器提供标识
setAllowedNetworkTypes(int flags)  //设置允许使用的网络类型,目前仅有两种定义,分别为NETWORK_MOBILE(移动网络)和NETWORK_WIFI,可以使用"|"运算
setAllowedOverMetered(boolean allow)  //是否允许“计量式的网络连接”执行下载操作,默认是true允许的
setAllowedOverRoaming(boolean allowed)  //是否允许使用漫游,默认是true允许的
setDescription(CharSequence description)  //设置Notification的描述信息
setDestinationInExternalFilesDir(Context context, String dirType, String subPath)  //设置将文件存储在【data/data/包名/files】目录;第2个参数是files目录下新建目录的目录名,不存在会自己创建;第3个参数是文件名,如果第3个参数带路径,要确保路径存在;不设置会存在【data/data/com.Android.provider.downloads/cache/】下面
setDestinationInExternalPublicDir(String dirType, String subPath)  //设置将文件存储在sd卡目录
setDestinationUri(Uri uri)  //设置需要下载目标的Uri,可以是http、ftp等等了
setMimeType(String mimeType)  //设置mime类型,这里看服务器配置,一般国家化的都为utf-8编码
setShowRunningNotification(boolean show)  //是否显示下载进度的提示,设为false需要权限【DOWNLOAD_WITHOUT_NOTIFICATION】,已经被废弃了
setNotificationVisibility(int visibility)  //设置Notification的显示和隐藏。取值:DownloadManager.Request.VISIBILITY_VISIBLE(默认值,在下载任务执行的过程中显示,下载完成后自动消失),_VISIBLE_NOTIFY_COMPLETED(下载进行时和完成之后都会显示),_HIDDEN(权限),_VISIBLE_NOTIFY_ONLY_COMPLETION(只有当任务完成时才会显示)
setTitle(CharSequence title)  //设置Notification的标题
setVisibleInDownloadsUi(boolean isVisible)  //设置下载管理类在处理过程中的界面是否显示。Set whether this download should be displayed in the system‘s Downloads UI. True by default
技术分享
Google还提供了一个简单的方法来实例化本类,这个构造方法为DownloadManager.Request(Uri uri) ,我们直接填写一个Uri即可,上面的设置使用默认情况。

DownloadManager.Query类
对于当前下载内容的状态,我们可以使用DownloadManager.Query类来获取,本类比较简单,仅仅提供了两个方法。
setFilterById(long... ids)  //根据id来过滤查找
setFilterByStatus(int flags) //根据任务的状态来查找
 技术分享

定义的常量
下载的状态完成均是以广播的形式通知大家,目前定义了下面三种Action:
ACTION_DOWNLOAD_COMPLETE//下载完成的动作
ACTION_NOTIFICATION_CLICKED //当用户单击notification中下载管理的某项时触发
ACTION_VIEW_DOWNLOADS //查看下载项

设置Notification的显示和隐藏:
DownloadManager.Request.VISIBILITY_VISIBLE//默认值,在下载任务执行的过程中显示,下载完成后自动消失
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED//下载进行时和完成之后都会显示
DownloadManager.Request.VISIBILITY_HIDDEN//将不会显示,如果设置该属性的话,必须添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION//只有当任务完成时才会显示  

DownloadManager类提供的query方法返回一个Cursor对象,其中定义的属性有以下几种:
技术分享
任务目前的状态保存在这个游标的COLUMN_STATUS 字段中,任务的状态有:
STATUS_FAILED  //失败
STATUS_PAUSED  //暂停
STATUS_PENDING  //等待将开始
STATUS_RUNNING  //正在处理中
STATUS_SUCCESSFUL  //已经下载成功

对于一个尚未完成的项,在Cursor中我们查找COLUMN_REASON字段,可能是以下定义:
ERROR_CANNOT_RESUME  //不能够继续,由于一些其他原因
ERROR_DEVICE_NOT_FOUND  //外部存储设备没有找到,比如SD卡没有插入
ERROR_FILE_ALREADY_EXISTS  //要下载的文件已经存在了,下载管理类是不会覆盖已经存在的文件,所以如果需要重新下载,请先删除以前的文件
ERROR_FILE_ERROR  //可能由于SD卡原因导致了文件错误
ERROR_HTTP_DATA_ERROR  //在Http传输过程中出现了问题
ERROR_INSUFFICIENT_SPACE  //由于SD卡空间不足造成的
ERROR_TOO_MANY_REDIRECTS  //这个Http有太多的重定向,导致无法正常下载
ERROR_UNHANDLED_HTTP_CODE  //无法获取http出错的原因,比如说远程服务器没有响应
ERROR_UNKNOWN  //未知的错误类型

有关暂停的一些状态,同样COLUMN_REASON字段的值,可能是以下定义:
PAUSED_QUEUED_FOR_WIFI  //由于移动网络数据问题,等待WiFi连接能用后再重新进入下载队列。
PAUSED_UNKNOWN  //未知原因导致了任务下载的暂停.
PAUSED_WAITING_FOR_NETWORK  //可能由于没有网络连接而无法下载,等待有可用的网络连接恢复。.
PAUSED_WAITING_TO_RETRY  //由于重重原因导致下载暂停,等待重试。 

关于下载目录
指定下载位置,及文件名称
1、request.setDestinationInExternalFilesDir(context,"TestDownload","Test.apk")
官方说明:Set the local destination for the downloaded file to a path within the application‘s external files directory (as returned by getExternalFilesDir(String).
翻译:给下载文件 “制定” 一个路径,文件路径的“特性”跟 getExternalFilesDir(String)类似。
关于getExternalFilesDir(String):
这个方法的返回值是一个文件夹,这个文件夹是被创建在【/data/data/包名/files/】目录下的,他一般是用来存储你的app运行所需的文件的(如图片的缓存)。默认情况下这个文件夹只有当前app有访问权限,当你的应用程序被卸载之后这个文件夹中的数据也会被清除。

2、request.setDestinationInExternalPublicDir("TestDownload","Test.apk");
官方说明:Set the local destination for the downloaded file to a path within the public external storage directory (as returned by getExternalStoragePublicDirectory(String)).
翻译:这个方法也是用来“制定”一个路径的,这个路径的特性类似于getExternalStoragePublicDirectory(String))
关于getExternalStoragePublicDirectory(String):
这个方法的返回值是一个文件夹,这个文件夹是被创建在你的SD卡根目录的【mnt/sdcard/】
这个文件夹中的内容其他程序都是可以访问的,当你的应用程序被卸载的时候,这个文件夹中的内容不会丢失。

通过Context.getExternalFilesDir()方法可以获取到 SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据
通过Context.getExternalCacheDir()方法可以获取到 SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
如果使用上面的方法,当你的应用在被用户卸载后,SDCard/Android/data/你的应用的包名/ 这个目录下的所有文件都会被删除,不会留下垃圾信息。
而且上面二个目录分别对应 设置->应用->应用详情里面的”清除数据“与”清除缓存“选项
如果要保存下载的内容,就不要放在以上目录下

Activity
public class DownloadManagerActivity extends ListActivity {
    public static final String URL_SMALL_FILE = "http://f2.market.xiaomi.com/download/AppStore/0b6c25446ea80095219649f646b8d67361b431127/com.wqk.wqk.apk";
    public static final String URL_BIG_FILE = "http://f3.market.xiaomi.com/download/AppChannel/099d2b4f6006a4c883059f459e0025a3e1f25454e/com.pokercity.bydrqp.mi.apk";
    private DownloadCompleteReceiver receiver;
    private TextView tv_info;
    private int status = 1;
    public static final int MSG_WHAT_DOWNLOAD_ID = 1;
    private boolean isQueryDownTaskById = false;
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_WHAT_DOWNLOAD_ID:
                if (isQueryDownTaskById) {
                    long id = (long) msg.obj;
                    tv_info.append("查询结果\n");
                    Map<String, String> map = queryDownTaskById(DownloadManagerActivity.this, id);
                    for (Map.Entry<String, String> k_v : map.entrySet()) {//增强for遍历键值对
                        tv_info.append(k_v + "\n");
                    }
                }
                break;
            }
        };
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = { "注册接收DownloadManager三个广播""取消注册广播接收者"//
                "下载文件到SD卡下的Download目录""下载文件到SD卡指定目录""下载文件到/data/data/包名/files/目录"//
                "通过状态查询下载任务""通过id查询下载任务", };
        for (int i = 0; i < array.length; i++) {
            array[i] = i + "、" + array[i];
        }
        tv_info = new TextView(this);// 将内容显示在TextView中
        tv_info.setTextColor(Color.BLUE);
        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
        tv_info.setPadding(20, 10, 20, 10);
        getListView().addFooterView(tv_info);
        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1new ArrayList<String>(Arrays.asList(array))));
        //加入到ThreadLocal中
        HandlerManager.setHandler(mHandler);
    }
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
        case 0:
            if (receiver == nullreceiver = new DownloadCompleteReceiver();
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);//下载完成的动作
            intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);//当用户单击notification中下载管理的某项时触发
            intentFilter.addAction(DownloadManager.ACTION_VIEW_DOWNLOADS);//查看下载项
            registerReceiver(receiver, intentFilter);
            break;
        case 1:
            if (receiver != null) {
                unregisterReceiver(receiver);
                receiver = null;
            }
            break;
        case 2:
            simpleDownLoadFileToSdNoUI(thisURL_SMALL_FILE);
            break;
        case 3:
            downLoadFile(thisURL_BIG_FILE"包青天"new Random().nextInt(1000) + ".apk", DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED//
                    DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILEtrue);
            break;
        case 4:
            simpleDownLoadFileToDataNoUI(thisURL_SMALL_FILE);
            break;
        case 5:
            tv_info.setText("\n" + getStatusString(status) + "    的任务有:");
            List<Map<String, String>> runningList = queryDownTaskByStatus(thisstatus);//共有五种状态
            // 遍历
            for (Map<String, String> hm : runningList) {
                tv_info.append("\n**********************************");
                Set<String> set = hm.keySet();
                for (String key : set) {
                    String value = hm.get(key);
                    tv_info.append("\n" + key + "-" + value);
                }
            }
            //下一个状态
            status = status << 1;
            if (status > (1 << 4)) status = 1;
            break;
        case 6:
            isQueryDownTaskById = true;
            break;
        }
    }
    public static String getStatusString(int status) {
        switch (status) {
        case DownloadManager.STATUS_FAILED:
            return "失败";
        case DownloadManager.STATUS_PAUSED:
            return "暂停";
        case DownloadManager.STATUS_PENDING:
            return "等待";
        case DownloadManager.STATUS_RUNNING:
            return "正在下载";
        case DownloadManager.STATUS_SUCCESSFUL:
            return "下载成功";
        default:
            return "没有这种状态";
        }
    }
    public static void simpleDownLoadFileToSdNoUI(Context mContext, String url) {
        downLoadFile(mContext, url, nullnull, DownloadManager.Request.VISIBILITY_HIDDEN, DownloadManager.Request.NETWORK_WIFItrue);
    }
    public static void simpleDownLoadFileToDataNoUI(Context mContext, String url) {
        downLoadFile(mContext, url, nullnull, DownloadManager.Request.VISIBILITY_HIDDEN, DownloadManager.Request.NETWORK_WIFIfalse);
    }
    /**
     * 使用DownloadManager下载文件
     * @param mContext    上下文
     * @param url                文件路径,请自行确保路径正确
     * @param filePath        子目录名,设为null则不创建子目录
     * @param fileName    文件名,设为null则使用服务器路径中的文件名
     * @param visibility        通知显示的类型,请使用DownloadManager.Request中定义的常量,设置为不显示需要权限DOWNLOAD_WITHOUT_NOTIFICATION
     * @param networkType    只允许在指定的网络类型下下周,请使用DownloadManager.Request中定义的常量,可以使用"|"运算
     * @param isToSdCard    是否保存到SD卡上,设为false则保存到【/data/data/包名/files/】目录下
     */
    public static void downLoadFile(Context mContext, String url, String filePath, String fileName, int visibility, int networkType, boolean isToSdCard) {
        if (url == null || url == "") url = "http://www.sinaimg.cn/dy/slidenews/3_img/2016_22/77542_379697_224394.jpg";
        if (filePath == null || filePath == "") filePath = Environment.DIRECTORY_DOWNLOADS;
        if (fileName == null || fileName == "") fileName = url.substring(url.lastIndexOf("/") + 1);//截取文件名及后缀名
        if (fileName == null || fileName == "") fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date());//截取失败时自动命名
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)).setNotificationVisibility(visibility).setAllowedNetworkTypes(networkType);
        if (isToSdCard) request.setDestinationInExternalPublicDir(filePath, fileName);//以SD卡路径为根路径
        else request.setDestinationInExternalFilesDir(mContext, filePath, fileName);//以【/data/data/包名/files/】为根路径
        ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).enqueue(request);//将下载请求放入队列
    }
    public static List<Map<String, String>> queryDownTaskByStatus(Context mContext, int status) {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterByStatus(status);
        Cursor cursor = ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).query(query);
        if (cursor != null) {
            List<Map<String, String>> data = new ArrayList<Map<String, String>>();
            while (cursor.moveToNext()) {
                String id = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
                String title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
                String uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                String name = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
                //String mStatu = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                String sizeNow = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));//已下载
                String sizeTotal = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                Map<String, String> map = new HashMap<String, String>();
                map.put("id", id);
                map.put("title", title);
                map.put("name", name);
                map.put("uri", uri);
                map.put("status", sizeTotal + ":" + sizeNow);
                data.add(map);
            }
            cursor.close();
            return data;
        }
        return null;
    }
    public static Map<String, String> queryDownTaskById(Context mContext, long id) {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(id);
        Cursor cursor = ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).query(query);
        if (cursor != null) {
            Map<String, String> data = new HashMap<String, String>();
            if (cursor.moveToNext()) {
                String sizeNow = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                String sizeTotal = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                data.put("sizeNow", sizeNow);
                data.put("sizeTotal", sizeTotal);
            }
            cursor.close();
            return data;
        }
        return null;
    }
}

广播
/** 注册一个广播接收器,当下载完毕后会收到一个android.intent.action.DOWNLOAD_COMPLETE的广播,在这里取出队列里下载任务,进行安装*/
public class DownloadCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
        Handler mHandler = HandlerManager.getHandler();
        mHandler.sendMessage(Message.obtain(mHandler, DownloadManagerActivity.MSG_WHAT_DOWNLOAD_ID, id));
        if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
            Toast.makeText(context, "编号 " + id + " 的下载任务已经完成!", Toast.LENGTH_SHORT).show();
        } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
            Toast.makeText(context, "别瞎点-" + id, Toast.LENGTH_SHORT).show();
        } else if (intent.getAction().equals(DownloadManager.ACTION_VIEW_DOWNLOADS)) {
            Toast.makeText(context, "查看下载项-" + id, Toast.LENGTH_SHORT).show();
        }
        //如果下载的是APK文件,则自动安装
        DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
        Query query = new DownloadManager.Query();
        query.setFilterById(id);
        Cursor cursor = downloadManager.query(query);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                String name = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
                String uri = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));
                String end = name.substring(name.lastIndexOf("."));
                if (end != null && end.equals(".apk") && status == DownloadManager.STATUS_SUCCESSFUL) {
                    Intent mIntent = new Intent(Intent.ACTION_VIEW);
                    mIntent.setDataAndType(Uri.parse(uri), "application/vnd.android.package-archive");
                    mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(mIntent);
                }
            }
            cursor.close();
        }
    }
}

UI线程共享的Handler
/**在UI线程中均可以使用此Handler,如Activity、Service、BroadcastReceiver,他们用的都是同一个对象,可以方便在相互通讯
 * ThreadLocal会为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
 */
public class HandlerManager {
    private static ThreadLocal<Handler> threadLocal = new ThreadLocal<Handler>();
    public static Handler getHandler() {
        return threadLocal.get();//返回当前线程所对应的线程局部变量
    }
    public static void setHandler(Handler mHandler) {
        threadLocal.set(mHandler);//设置当前线程的线程局部变量的值
    }
}





DownloadManager 下载管理类

标签:

原文地址:http://www.cnblogs.com/baiqiantao/p/5562008.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!