DownloadProvider 源码详细分析
- DownloadProvider 简介
- DownloadProvider 是Android提供的DownloadManager的增强版,亮点是支持断点下载,提供了“开始下载”,“暂停下载”,“重新下载”,“删除下载”接口。源码下载地址
DownloadProvider 详细分析
DownloadProvider开始下载的是由DownloadManager 的 enqueue方法启动的,启动一个新的下载任务的时序图
开始新的下载时候会调用DownloadManager的enqueue方法,然后再执行DownloadProvider的insert方法,将下载信息写入数据库,包括下载链接地址等,然后再调用DownloadService的onCreate或者onStartCommand方法。 DownloadProvider类是非常重要的类,所有操作都跟此类有关,因为要保存下载状态。 在分析DownloadProvider的insert方法前,先看看insert方法的源码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061@Override
public
Uri insert(
final
Uri uri,
final
ContentValues values) {
checkInsertPermissions(values);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
// note we disallow inserting into ALL_DOWNLOADS
int
match = sURIMatcher.match(uri);
if
(match != MY_DOWNLOADS) {
Log.d(Constants.TAG,
"calling insert on an unknown/invalid URI: "
+ uri);
throw
new
IllegalArgumentException(
"Unknown/Invalid URI "
+ uri);
}
ContentValues filteredValues =
new
ContentValues();
......
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(
new
Intent(context, DownloadService.
class
));
//插入数据库
long
rowID = db.insert(DB_TABLE,
null
, filteredValues);
if
(rowID == -
1
) {
Log.d(Constants.TAG,
"couldn't insert into downloads database"
);
return
null
;
}
insertRequestHeaders(db, rowID, values);
//启动下载服务
context.startService(
new
Intent(context, DownloadService.
class
));
notifyContentChanged(uri, match);
return
ContentUris.withAppendedId(Downloads.CONTENT_URI, rowID);
}
每次开始一个新的下载任务,都会插入数据库,然后启动启动下载服务类DownloadService,所以真正处理下载的是后台服务DownloadService,DownloadService中有个下载线程类DownloadThread,DownloadService时序图
如果DownloadService没有启动将会执行onCreate()------>onStartCommand()方法,否则执行onStartCommand()方法。然后执行updateFromProvider()方法启动UpdateThread线程,准备启动DownloadThread线程。 分析UpdateThread的run方法前先看看run方法的源码:
1234567891011public
void
run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//如果数据里的存储的达到了1000以上时候,将会删除status>200即失败的记录
trimDatabase();
removeSpuriousFiles();
boolean
keepService =
false
;
// for each update from the database, remember which download is
// supposed to get restarted soonest in the future
long
wakeUp = Long.MAX_VALUE;
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788//会一直在此循环,直到启动完所有下载任务
for
(;;) {
synchronized
(DownloadService.
this
) {
if
(mUpdateThread !=
this
) {
throw
new
IllegalStateException(
"multiple UpdateThreads in DownloadService"
);
}
if
(!mPendingUpdate) {
mUpdateThread =
null
;
if
(!keepService) {
stopSelf();
}
if
(wakeUp != Long.MAX_VALUE) {
scheduleAlarm(wakeUp);
}
return
;
}
mPendingUpdate =
false
;
}
long
now = mSystemFacade.currentTimeMillis();
keepService =
false
;
wakeUp = Long.MAX_VALUE;
Set<
long
> idsNoLongerInDatabase =
new
HashSet<
long
>(
mDownloads.keySet());
Cursor cursor = getContentResolver().query(
Downloads.ALL_DOWNLOADS_CONTENT_URI,
null
,
null
,
null
,
null
);
if
(cursor ==
null
) {
continue
;
}
try
{
DownloadInfo.Reader reader =
new
DownloadInfo.Reader(
getContentResolver(), cursor);
int
idColumn = cursor.getColumnIndexOrThrow(Downloads._ID);
for
(cursor.moveToFirst(); !cursor.isAfterLast(); cursor
.moveToNext()) {
long
id = 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
;
}
long
next = info.nextAction(now);
if
(next ==
0
) {
keepService =
true
;
}
else
if
(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
// permanently
for
(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
随机推荐
- CF1146 Forethought Future Cup Elimination Round Tutorial
CF1146 Forethought Future Cup Elimination Round Tutorial 叮,守夜冠军卡 https://codeforces.com/blog/entry/6 ...
- InnoDB 文件系统
1. 操作系统文件系统inode 2. InnoDB的存储结构 2.1Innodb inode page 参考 http://mysql.taobao.org/monthly/2016/02/01/ ...
- Python零基础入门(安装步骤,验证方式, 简单操作)
本篇文章适合新人小白初步了解Python,涵盖Python的介绍.安装以及简单的基础操作. 1.Python简介 Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言.它的设 ...
- require.ensure的用法;异步加载-代码分割;
webpack异步加载的原理 webpack ensure相信大家都听过.有人称它为异步加载,也有人说做代码切割,那这 个家伙到底是用来干嘛的?其实说白了,它就是把js模块给独立导出一个.js文件的, ...
- 《Linux内核设计与实现》读书笔记 18
第十八章调试 18.1 准备开始 一个bug:大部分bug通常都不是行为可靠而且定义明确的 一个藏匿bug的内核版本:找出bug首先出现的版本 相关内核代码的知识和运气 18.2内核中的bug 可以有 ...
- 小学四则运算APP 第三阶段冲刺-第一天
团队成员:陈淑筠.杨家安.陈曦 团队选题:小学四则运算APP 第三次冲刺阶段时间:12.12~12.19 本次发布的是音乐播放功能,可以根据用户需求一边播放音乐一边做题,也拥有暂停播放音乐的功能,增强 ...
- 11-Python3从入门到实战—基础之生成器和迭代器
Python从入门到实战系列--目录 切片 Python提供切片(Slice)操作符用来获取列表.元组等数据中的部分元素:如,读取列表 list[m:n]:表示获取m-n区间的元素 list[m:n: ...
- git学习笔记——廖雪峰git教程
OK,先附上教程--廖雪峰的官方网站 友情连接:git官网 简介 这里我只想引用他的原文: Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的.实际情况是这样的: L ...
- Linux添加目录到环境变量以及添加sublime到环境变量
博主之前有过这种情况,就是在普通用户下su ls等命令还有效,可登陆进root用户之后这些常用的命令竟然失效了. 像这样 这问题其实很简单,但是对于不清楚环境变量的配置的同学来说也的确棘手,我之前就是 ...
- NullPointerException-----开发中遇到的空指针异常
1.使用CollectionUtils.isEmpty判断空集合 public class TestIsEmpty { static class Person{} static class Girl ...