DownloadProvider 源码详细分析
- DownloadProvider 简介
- DownloadProvider 是Android提供的DownloadManager的增强版,亮点是支持断点下载,提供了“开始下载”,“暂停下载”,“重新下载”,“删除下载”接口。源码下载地址
DownloadProvider 详细分析
DownloadProvider开始下载的是由DownloadManager 的 enqueue方法启动的,启动一个新的下载任务的时序图

开始新的下载时候会调用DownloadManager的enqueue方法,然后再执行DownloadProvider的insert方法,将下载信息写入数据库,包括下载链接地址等,然后再调用DownloadService的onCreate或者onStartCommand方法。 DownloadProvider类是非常重要的类,所有操作都跟此类有关,因为要保存下载状态。 在分析DownloadProvider的insert方法前,先看看insert方法的源码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061@OverridepublicUri insert(finalUri uri,finalContentValues values) {checkInsertPermissions(values);SQLiteDatabase db = mOpenHelper.getWritableDatabase();// note we disallow inserting into ALL_DOWNLOADSintmatch = sURIMatcher.match(uri);if(match != MY_DOWNLOADS) {Log.d(Constants.TAG,"calling insert on an unknown/invalid URI: "+ uri);thrownewIllegalArgumentException("Unknown/Invalid URI "+ uri);}ContentValues filteredValues =newContentValues();......Integer dest = values.getAsInteger(Downloads.COLUMN_DESTINATION);if(dest !=null) {......}Integer vis = values.getAsInteger(Downloads.COLUMN_VISIBILITY);if(vis ==null) {if(dest == Downloads.DESTINATION_EXTERNAL) {filteredValues.put(Downloads.COLUMN_VISIBILITY,Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);}else{filteredValues.put(Downloads.COLUMN_VISIBILITY,Downloads.VISIBILITY_HIDDEN);}}else{filteredValues.put(Downloads.COLUMN_VISIBILITY, vis);}......String pckg = values.getAsString(Downloads.COLUMN_NOTIFICATION_PACKAGE);String clazz = values.getAsString(Downloads.COLUMN_NOTIFICATION_CLASS);if(pckg !=null&& (clazz !=null|| isPublicApi)) {......}......//启动下载服务Context context = getContext();context.startService(newIntent(context, DownloadService.class));//插入数据库longrowID = db.insert(DB_TABLE,null, filteredValues);if(rowID == -1) {Log.d(Constants.TAG,"couldn't insert into downloads database");returnnull;}insertRequestHeaders(db, rowID, values);//启动下载服务context.startService(newIntent(context, DownloadService.class));notifyContentChanged(uri, match);returnContentUris.withAppendedId(Downloads.CONTENT_URI, rowID);}每次开始一个新的下载任务,都会插入数据库,然后启动启动下载服务类DownloadService,所以真正处理下载的是后台服务DownloadService,DownloadService中有个下载线程类DownloadThread,DownloadService时序图

如果DownloadService没有启动将会执行onCreate()------>onStartCommand()方法,否则执行onStartCommand()方法。然后执行updateFromProvider()方法启动UpdateThread线程,准备启动DownloadThread线程。 分析UpdateThread的run方法前先看看run方法的源码:
1234567891011publicvoidrun() {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//如果数据里的存储的达到了1000以上时候,将会删除status>200即失败的记录trimDatabase();removeSpuriousFiles();booleankeepService =false;// for each update from the database, remember which download is// supposed to get restarted soonest in the futurelongwakeUp = Long.MAX_VALUE;12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788//会一直在此循环,直到启动完所有下载任务for(;;) {synchronized(DownloadService.this) {if(mUpdateThread !=this) {thrownewIllegalStateException("multiple UpdateThreads in DownloadService");}if(!mPendingUpdate) {mUpdateThread =null;if(!keepService) {stopSelf();}if(wakeUp != Long.MAX_VALUE) {scheduleAlarm(wakeUp);}return;}mPendingUpdate =false;}longnow = mSystemFacade.currentTimeMillis();keepService =false;wakeUp = Long.MAX_VALUE;Set<long> idsNoLongerInDatabase =newHashSet<long>(mDownloads.keySet());Cursor cursor = getContentResolver().query(Downloads.ALL_DOWNLOADS_CONTENT_URI,null,null,null,null);if(cursor ==null) {continue;}try{DownloadInfo.Reader reader =newDownloadInfo.Reader(getContentResolver(), cursor);intidColumn = cursor.getColumnIndexOrThrow(Downloads._ID);for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {longid = cursor.getLong(idColumn);idsNoLongerInDatabase.remove(id);DownloadInfo info = mDownloads.get(id);if(info !=null) {updateDownload(reader, info, now);}else{info = insertDownload(reader, now);}if(info.hasCompletionNotification()) {keepService =true;}longnext = info.nextAction(now);if(next ==0) {keepService =true;}elseif(next >0&& next < wakeUp) {wakeUp = next;}}}finally{cursor.close();}for(Long id : idsNoLongerInDatabase) {deleteDownload(id);}// is there a need to start the DownloadService? yes, if there// are rows to be deleted.for(DownloadInfo info : mDownloads.values()) {if(info.mDeleted) {keepService =true;break;}}mNotifier.updateNotification(mDownloads.values());// look for all rows with deleted flag set and delete the rows// from the database// permanentlyfor(DownloadInfo info : mDownloads.values()) {if(info.mDeleted) {Helpers.deleteFile(getContentResolver(), info.mId,info.mFileName, info.mMimeType);}}}}</long></long>UpdateThread线程负责从数据库中获取下载任务,该线程不会每次都新建新的对象,只有UpdateThread为空时候才会新建,在此线程的run()方法中有两个for循环,外循环是从数据获取下载任务,确保插入数据库的任务都能被启动,直到启动完所有的下载任务才会退出。内循环是遍历从数据库获取的没完成或者刚开始的下载任务,启动下载。 DownloadThrea线程是真正负责下载的线程,每次启动一个任务都会创建一个新的下载线程对象(对手机来说会耗很大的CPU,所有要加上同步锁或者线程池来控制同时下载的数量),看看DownloadThread run方法的时序图:

从这个时序图可以看出,这里涉及的源码比较多,在这没有写,看的时候一定要对照的源码来看。其实在下载的时候会发生很多的异常,如网络异常,内存卡容量不足等,所以捕获异常很重要的,捕获后进行相关的处理,这也是体现出考虑全面的地方。
DownloadProvider 源码详细分析的更多相关文章
- LinkedHashMap 源码详细分析(JDK1.8)
1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...
- HashMap 源码详细分析(JDK1.8)
一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...
- ArrayList 源码详细分析
1.概述 ArrayList 是一种变长的集合类,基于定长数组实现.ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个 ...
- 一文读懂Spring动态配置多数据源---源码详细分析
Spring动态多数据源源码分析及解读 一.为什么要研究Spring动态多数据源 期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式 ...
- android_launcher的源码详细分析
转载请注明出处:http://blog.csdn.net/fzh0803/archive/2011/03/26/6279995.aspx 去年做了launcher相关的工作,看了很长时间.很多人都在修 ...
- 源码分析 ucosii/source 任务源码详细分析
分析源码: 得先学会读文档, 函数前边的 note :是了解该程序员的思想的途径.不得不重视 代码前边的 Notes,了解思想后,然后在分析代码时看他是如何具体实现的. 1. ucosii/sour ...
- SpringMvc demo示例及源码详细分析
三层架构介绍 我们的开发架构一般都是基于两种形式,一种C/S架构,也就是客户端/服务器,另一种是B/S架构,也就是浏览器/服务器.在JavaEE开发中,几乎全部都是基于B/S架构的开发.那么在B/S架 ...
- Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗
开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...
- dede源码详细分析之--全局变量覆盖漏洞的防御
http://blog.csdn.net/ebw123/article/details/8100594
随机推荐
- 2.RapidIO串行物理层的包与控制符号
转自https://www.cnblogs.com/liujinggang/p/9932150.html 一.RapidIO串行物理层背景介绍 上篇博文提到RapidIO的物理层支持串行物理层与并行物 ...
- Jlink使用技巧之合并烧写文件
前言 IAP(In-application-programming),即在应用中编程.当产品发布之后,可以通过网络方便的升级固件程序,而不需要拆机下载程序.IAP系统的固件一般由两部分组成,即Boot ...
- vue-cli 3.0 axios 跨域请求代理配置及生产环境 baseUrl 配置
1. 开发环境跨域配置 在 vue.config.js 文件中: module.exports = { runtimeCompiler: true, publicPath: '/', // 设置打包文 ...
- Java开源博客My-Blog之mysql容器重复初始化的严重bug修复过程
写在前面的话 <Docker+SpringBoot+Mybatis+thymeleaf的Java博客系统开源啦> <Java开源博客My-Blog之docker容器组件化修改> ...
- 谈谈css伪类与伪元素
前端er们大都或多或少地接触过CSS伪类和伪元素,比如最常见的:focus.:hover以及<a>标签的:link.:visited等,伪元素较常见的比如:before.:after等. ...
- asp.net mvc接收安卓post的json字符串
筛选器: using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syst ...
- 2016-03-22 OneZero团队 Daily Scrum Meeting
会议时间: 2016-03-22 9:33-9:57am 会议内容: 一.在原有Sprint Backlog基础上,我们加了亮点(摇一摇功能:随机选取一条记录在界面显示,以提醒主页君回忆) 需求分析图 ...
- 一些调格式的经验 & 插入图注和尾注
一些调格式的经验(以Word2010为例) 1. 从目录正文分别编页码 将光标放在要重新编写页码起始页的最开始位置 分节:页面布局->分隔符->分节符(连续) 插入页码后,选中页码起始页页 ...
- python 函数及变量作用域及装饰器decorator @详解
一.函数及变量的作用 在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...
- shell脚本--shift参数左移
参数左移什么意思呢?这个参数指的是在运行脚本时,跟在脚本名后面的参数,前面已经讲过,可以使用$#来获取参数的个数,使用$*来获取所有的参数,而参数左移的含义是这样的:有个指针指向参数列表第一个参数,左 ...