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
@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方法的源码:

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方法的时序图: 
从这个时序图可以看出,这里涉及的源码比较多,在这没有写,看的时候一定要对照的源码来看。其实在下载的时候会发生很多的异常,如网络异常,内存卡容量不足等,所以捕获异常很重要的,捕获后进行相关的处理,这也是体现出考虑全面的地方。

DownloadProvider 源码详细分析的更多相关文章

  1. LinkedHashMap 源码详细分析(JDK1.8)

    1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...

  2. HashMap 源码详细分析(JDK1.8)

    一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...

  3. ArrayList 源码详细分析

    1.概述 ArrayList 是一种变长的集合类,基于定长数组实现.ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个 ...

  4. 一文读懂Spring动态配置多数据源---源码详细分析

    Spring动态多数据源源码分析及解读 一.为什么要研究Spring动态多数据源 ​ 期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式 ...

  5. android_launcher的源码详细分析

    转载请注明出处:http://blog.csdn.net/fzh0803/archive/2011/03/26/6279995.aspx 去年做了launcher相关的工作,看了很长时间.很多人都在修 ...

  6. 源码分析 ucosii/source 任务源码详细分析

    分析源码: 得先学会读文档, 函数前边的 note :是了解该程序员的思想的途径.不得不重视 代码前边的  Notes,了解思想后,然后在分析代码时看他是如何具体实现的. 1. ucosii/sour ...

  7. SpringMvc demo示例及源码详细分析

    三层架构介绍 我们的开发架构一般都是基于两种形式,一种C/S架构,也就是客户端/服务器,另一种是B/S架构,也就是浏览器/服务器.在JavaEE开发中,几乎全部都是基于B/S架构的开发.那么在B/S架 ...

  8. Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗

    开心一刻 吃完晚饭,坐在院子里和父亲聊天 父亲:你有什么人生追求? 我:金钱和美女 父亲对着我的头就是一丁弓,说道:小小年纪,怎么这么庸俗,重说一次 我:事业与爱情 父亲赞赏的摸了我的头,说道:嗯嗯, ...

  9. dede源码详细分析之--全局变量覆盖漏洞的防御

    http://blog.csdn.net/ebw123/article/details/8100594

随机推荐

  1. 事务,acid,cap,paxos随笔

    事务ACID四个特性: A:原子性(Atomicity)C:一致性(Consistency)I:隔离性(Isolation)D:持久性(Durability) 原子性:语句要么全执行,要么全不执行,是 ...

  2. windows下pwd、ls、tail-f命令使用

    一.问题 习惯了linux命令,在windows上使用cmd没有这些命令时很不习惯. 二.解决办法 2.1 找到这些命令对应的windows命令 ls,对应于windows的dir pwd,对应于wi ...

  3. Git push 时如何避免出现 "Merge branch 'master' of ..."

    在使用 Git 的进行代码版本控制的时候,往往会发现在 log 中出现 "Merge branch 'master' of ..." 这句话,如下图所示.日志中记录的一般为开发过程 ...

  4. Runaway argument错误 [Overleaf: 在线Latex] [Type 3问题后续]

    [背景与问题描述] 在Latex中,经常出现各种问题: Runaway argument? {\contentsline {subsection}{\numberline {6.3}General c ...

  5. jenkins 上命令各种找不到问题

    代码: 兵马未动,粮草先行 作者: 传说中的汽水枪 如有错误,请留言指正,欢迎一起探讨. 转载请注明出处.   在使用jenkins的时候,会使用一些命令行,有的时候明明在电脑的命令行(终端)可以执行 ...

  6. C. Ehab and a 2-operation task

    链接 [https://codeforces.com/contest/1088/problem/C] 题意 n个数,最多n+1操作,要么前i个数加x,要么前i个数对x取余,最后使得严格递增 分析 直接 ...

  7. 附加题(一)——interesting的抄袭现象

    这次的作业很有意思,是让我们搜索抄袭的同学的个人总结,并让我们列出链接,时间,作者及原因.其实我不是很能理解,抄袭现象是可耻的不受推崇的这是无可厚非,但是为什么要求我们大部分没有抄袭的同学去探究抄袭同 ...

  8. 网络:OSPF理解

    OSPF(开放最短路径优先)协议使用Dijkstra算法,常见的版本有:OSPFv2.OSPFv3等.以下主要介绍OSPFv2,OSPFv3是面向IPv6的且不兼容IPv4. 1.工作过程: 1)每台 ...

  9. python中的hasattr()、getattr()、setattr()

    hasattr()的用法和理解--hasattr(obj, target) 判断对象obj中是否含有,目标target属性,然后返回布尔值,如果有返回True,没有返回False. >>& ...

  10. Mac+Docker环境下xdebug的配置

    由于容器化的需要,前几天我本地也换成了docker环境.就研究了一下docker环境下phpstorm和xdebug的配置. http://www.mmfei.com/?p=453 这个博客给出了一个 ...