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

开始新的下载时候会调用DownloadManager的enqueue方法,然后再执行DownloadProvider的insert方法,将下载信
息写入数据库,包括下载链接地址等,然后再调用DownloadService的onCreate或者onStartCommand方法。
DownloadProvider类是非常重要的类,所有操作都跟此类有关,因为要保存下载状态。
在分析DownloadProvider的insert方法前,先看看insert方法的源码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
@Overridepublic 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方法的源码:
|
1
2
3
4
5
6
7
8
9
10
11
|
public 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; |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
//会一直在此循环,直到启动完所有下载任务 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方法的时序图:

从这个时序图可以看出,这里涉及的源码比较多,在这没有写,看的时候一定要对照的源码来看。其实在下载的时候会发生很多的异常,如网络异常,内存卡容量不足等,所以捕获异常很重要的,捕获后进行相关的处理,这也是体现出考虑全面的地方。
Android DownloadProvider学习的更多相关文章
- Android DownloadProvider学习 (二)
DownloadManager.Request用来请求一个下载,DownloadManager.Query用来查询下载信息,这两个类的具体功能会在后面穿插介绍.DownloadManager的源码可见 ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习路线图
Android开发学习方法: Android是一个比较庞大的体系,从底层的Linux内核到上层的应用层,各部分的内容跨度也比较大.因此,一个好的学习方法对我们学习Android开发很重要. 在此建议, ...
- Android动画学习(二)——Tween Animation
前两天写过一篇Android动画学习的概述,大致的划分了下Android Animation的主要分类,没有看过的同学请移步:Android动画学习(一)——Android动画系统框架简介.今天接着来 ...
- Android自动化学习笔记:编写MonkeyRunner脚本的几种方式
---------------------------------------------------------------------------------------------------- ...
- Android自动化学习笔记之MonkeyRunner:官方介绍和简单实例
---------------------------------------------------------------------------------------------------- ...
- android开发学习笔记000
使用书籍:<疯狂android讲义>——李刚著,2011年7月出版 虽然现在已2014,可我挑来跳去,还是以这本书开始我的android之旅吧. “疯狂源自梦想,技术成就辉煌.” 让我这个 ...
- Android Animation学习(六) View Animation介绍
Android Animation学习(六) View Animation介绍 View Animation View animation系统可以用来执行View上的Tween animation和F ...
- Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition
Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...
随机推荐
- WCF初探文章列表
WCF初探-1:认识WCF WCF初探-6:WCF服务配置 WCF初探-2:手动实现WCF程序 WCF初探-7:WCF服务配置工具使用 WCF初探-3:WCF消息交换模式之单向模式 WCF初探-8:W ...
- Android编程思想双11口诀
能复制就复制,节约时间避免出错 保留原本结构,简单上手容易调试 说明随手可得,不用上网或打开文档 增加必要注释,说明功能和使用方法 命名尽量规范,容易查找一看就懂 函数尽量嵌套,减少代码容易修改 最先 ...
- Apache MiNa 实现多人聊天室
Apache MiNa 实现多人聊天室 开发环境: System:Windows JavaSDK:1.6 IDE:eclipse.MyEclipse 6.6 开发依赖库: Jdk1.4+.mina-c ...
- VisualSVN-5.1.4补丁原创发布
VisualSVN-5.1.4补丁原创发布 VisualSVN-5.1.4Patch.rar VisualSVN-5.1.4官方安装包.rar
- mac idea快捷键
新买的mac,如果默认使用idea快捷键,因为用eclipse,完全转不过来,所以荡下别人整理好的资源放在目录下,以备查看 原文:https://my.oschina.net/sunzy/blog/3 ...
- Android Context上下文解析
1.Context概念 Context,相信不管是第一天开发Android,还是开发Android的各种老鸟,对于Context的使用一定不陌生~~你在加载资源.启动一个新的Activity.获取系统 ...
- HelloWorld Makefile Template
DEPDIR = build_dep TARGET_NAME = helloworld CFLAGS = -Wall SRCS = main.c SRCS += foo.c OBJS = $(SRCS ...
- <textarea>使用的时候发现的两个问题的总结
在练习表单的过程中,使用<textarea>时,遇到2个问题: 1.文本开始前有好多空格. 原来的代码是这样的: <textarea row="20" col=& ...
- Windows Phone开发需要了解的背景
在Windows Phone8.1之前,Windows Phone应用是基于Silverlight的,这些代码也不能在Windows上运行,从Windows Phone8.1开始,开发者多了一个选择, ...
- POJ 3683 Priest John's Busiest Day (2-SAT)
题意:有n对新人要在同一天结婚.结婚时间为Ti到Di,这里有时长为Si的一个仪式需要神父出席.神父可以在Ti-(Ti+Si)这段时间出席也可以在(Di-Si)-Si这段时间.问神父能否出席所有仪式,如 ...