Android Download机制详解(一)DocumentUI部分
在Android中Google为我们集成了一套十分便利的Download机制,用来下载网络上的资源文件。以此省去了我们编写和维护大量与Download相关的代码。
组成
Android中Download由三个部分组成:
1.DocumentsUI -----> /frameworks/base/packages/DocumentsUI/
2.DownloadManager ---->/frameworks/base/core/java/android/app/
3.DownloadProvider ---->/packages/providers/DownloadProvider/
下图中用MVC的分层将这三部分做了划分:

其中DocumentsUI作为视图层(V)负责展示Download信息
DownloadManager和DownloadProvder的一部分作为控制层(C)负责下载的逻辑控制
DownloadProvder的另一部分则作为数据层(M)负责数据的存储
总流程

Download的整个流程在上图中已经表示的很明显了,这里不做过对赘述,详细讲解将放在后面。
值得注意的是DownloadManager看似是主宰整个下载过程的角色,但事实并非如此正真的幕后“黑手”是DownloadProvider。
详细分析
DocumentUI--数据显示篇
DocumentsUI是一个可见程序,但即便如此Launcher上也没有直接打开的DocumentsUI的入口。它的入口一般有两个:
- Launcher上的“下载”app
 - 被其他app唤起如(短信点击添加附件后唤起的app就是DocumentsUI)
 
这里我们只分析1这种情况,情况2的话感兴趣的同学可以自己学习一下。
“下载”这个app的代码被包含在了DownloadProvider中,具体位置如下:

上图中的ui文件夹就是包含“下载”app的所有代码.
根据AndroidManifest文件判断,点击“下载”app首先启动的activity是:
/packages/providers/DownloadProvider/ui/src/com/android/providers/downloads/ui/DownloadList.java
我们来看一下这个文件的内容:
17package com.android.providers.downloads.ui;
18import android.app.Activity;
20import android.content.Intent;
21import android.os.Bundle;
22import android.provider.DocumentsContract;
23import com.android.providers.downloads.Constants;
25public class DownloadList extends Activity {
27 @Override
28 public void onCreate(Bundle icicle) {
29 super.onCreate(icicle);
30
31 // Trampoline over to new management UI
32 final Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
33 intent.setData(DocumentsContract.buildRootUri(
34 Constants.STORAGE_AUTHORITY, Constants.STORAGE_ROOT_ID));
35 startActivity(intent);
36 finish();
37 }
38}
看到这里大家应该知道了,其实这个“下载”app只是一个传送门,传送门的另一边是Action包含“DocumentsContract.ACTION_MANAGE_ROOT”的Activity,那么这个神秘的Activity到底是何方神圣呢?
我想我不说大家也应该猜到了,这个Activity肯定是存在与DocumentsUI中,因为上面我们已经说到过了,DocumentUI是整个下载系统的视图层。那么下面就转战我们这一小节的主角DocumentsUI。

通过DocumentsUI的AndroidManifest文件知道,接受“下载”发出的Intent的Activity就是上面的DocumentsActivity,它的路径是
/frameworks/base/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
我将DocumentsActivity的启动流程分两步来分析
- 初始化状态信息
 - 查询和显示对应的数据
 
初始化状态信息的流程如下:

在onCreate方法中会调用buildDefaultState方法来初始化mState对象。State是专门存储状态信息的
223    private void buildDefaultState() {
224        mState = new State();
225
226        final Intent intent = getIntent();
227        final String action = intent.getAction();
228        if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
229            mState.action = ACTION_OPEN;
230        } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
231            mState.action = ACTION_CREATE;
232        } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
233            mState.action = ACTION_GET_CONTENT;
234        } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
235            mState.action = ACTION_OPEN_TREE;
236        } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
237            mState.action = ACTION_MANAGE;
238        }
239
240        if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
241            mState.allowMultiple = intent.getBooleanExtra(
242                    Intent.EXTRA_ALLOW_MULTIPLE, false);
243        }
244
245        if (mState.action == ACTION_MANAGE) {
246            mState.acceptMimes = new String[] { "*/*" };
247            mState.allowMultiple = true;
248        } else if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
249            mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
250        } else {
251            mState.acceptMimes = new String[] { intent.getType() };
252        }
253
254        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
255        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
256        mState.showAdvanced = mState.forceAdvanced
257                | LocalPreferences.getDisplayAdvancedDevices(this);
258
259        if (mState.action == ACTION_MANAGE) {
260            mState.showSize = true;
261        } else {
262            mState.showSize = LocalPreferences.getDisplayFileSize(this);
263        }
264    }
由于前面传入的action为“DocumentsContract.ACTION_MANAGE_ROOT”,这里会走236行将mState.action 设置为 ACTION_MANAGE
在Sate类中的restored变量初始值为false,所以在onCreate方法中会走下面这段代码
211        if (!mState.restored) {
212            if (mState.action == ACTION_MANAGE) {
213                final Uri rootUri = getIntent().getData();
214                new RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
215            } else {
216                new RestoreStackTask().execute();
217            }
218        } else {
219            onCurrentDirectoryChanged(ANIM_NONE);
220        }
通过调用RestoreRootTask来将当面信息保存下来,接着调用onRootPicked来对需要显示的内容做相应判断。
在onRootPicked中又会启动另一个异步任务PickRootTask,它主要作用是通过intent中的data信息来构建DocumentInfo的对象,该对象中主要保存一下字段
56 public String authority;
57 public String documentId;
58 public String mimeType;
59 public String displayName;
60 public long lastModified;
61 public int flags;
62 public String summary;
63 public long size;
64 public int icon;
这里的authority现在的值为"com.android.providers.downloads.documents",documentId的值为"downloads"
获得了以上信息后,PickRootTask的任务差不多就完成了,接着它会调用onCurrentDirectoryChanged来告诉DocumentsUI,“有人要显示所有下载的信息,我这边的信息处理完了你可以去查询和显示下载信息了”
查询和显示对应的数据流程如下

首先在onCreateView方法中初始化界面布局,接着在onActivityCreated中初始化Adapter和异步数据加载器,最后将从数据库取出来的数据与Adapter进行绑定。
这里我们重点看异步查询数据部分
 mCallbacks = new LoaderCallbacks<DirectoryResult>() {
262            @Override
263            public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
264                final String query = getArguments().getString(EXTRA_QUERY);
265
266                Uri contentsUri;
267                switch (mType) {
268                    case TYPE_NORMAL:
269                        contentsUri = DocumentsContract.buildChildDocumentsUri(
270                                doc.authority, doc.documentId);
271                        if (state.action == ACTION_MANAGE) {
272                            contentsUri = DocumentsContract.setManageMode(contentsUri);
273                        }
274                        return new DirectoryLoader(
275                                context, mType, root, doc, contentsUri, state.userSortOrder);
276                    //部分代码省略
289                }
290            }
291
292            @Override
293            public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
294                if (!isAdded()) return;
295
296                mAdapter.swapResult(result);
297
                   //部分代码省略
321
322                mLastSortOrder = state.derivedSortOrder;
323            }
324
325            @Override
326            public void onLoaderReset(Loader<DirectoryResult> loader) {
327                mAdapter.swapResult(null);
328            }
329        };
在之前的讲述中知道state.actio的值为ACTION_MANAGER,onCreateLoader会走271行代码,然后返回一个DirectoryLoader来进行数据的查询。当DirectoryLoader查询完数据后系统会回调onLoadFinished方法,最后通过
mAdapter.swapResult(result);来将数据与Adapter绑定。Adapter有了数据的更新自然就会去更新界面,那么此时从打开“下载”app到整个界面解显示就结束了。最终界面如下图:

总结一下,点击“下载”app主要经历一下一个步骤:
- 从DownloadList跳转到DocumentsActivity
 - 保存需要显示的内容信息
 - 通过DirectoryLoader完成异步查询数据
 - 显示数据
 
Android L 源代码在线查看http://androidxref.com/5.1.0_r1/
Android Download机制详解(一)DocumentUI部分的更多相关文章
- Android Binder机制详解:手写IPC通信
		
想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...
 - android Intent机制详解
		
http://www.oschina.net/question/565065_67909 http://www.cnblogs.com/hummersofdie/archive/2011/02/12/ ...
 - [转]android Intent机制详解
		
转自:http://blog.csdn.net/t12x3456/article/details/7688154 1.什么是Intent Intent是一种运行时绑定(run-time binding ...
 - android binder机制详解
		
摘要 Binder是android中一个很重要且很复杂的概念,它在系统的整体运作中发挥着极其重要的作用,不过本文并不打算从深层次分析Binder机制,有两点原因:1是目前网上已经有2篇很好的文章了,2 ...
 - android Handler机制详解
		
简单运行图: 名词解析: Message(消息):定义了一个包含描述以及随意的数据对象可以被发送到Hanlder的消息,获得消息的最好方法是Message.obtain或者Handler.o ...
 - Android Touch事件传递机制详解 下
		
尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...
 - Android事件传递机制详解及最新源码分析——ViewGroup篇
		
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...
 - Android 的事件传递机制,详解
		
Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...
 - Android应用AsyncTask处理机制详解及源码分析
		
1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...
 
随机推荐
- c#封装DBHelper类  c# 图片加水印  (摘)C#生成随机数的三种方法  使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象  c# 制作正方形图片  JavaScript 事件循环及异步原理(完全指北)
			
c#封装DBHelper类 public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...
 - Android给TextView设置透明背景、圆角边框
			
第一种方法:在drawable文件夹下新建一个文件设置背景样式 代码: 在drawable文件夹下面新建text_view_border.xml <?xml version="1.0& ...
 - SQL  将两个结构相同的表合并到成一个表
			
select * into 新表名 from (select * from T1 union all select * from T2) 这个语句可以实现将合并的数据追加到一个新表中. 不合并重复数据 ...
 - C#Project不生成.vhost.exe和.pdb文件的方法
			
编译C#工程时,在C#的Project的属性界面的Build选项卡中当Configuration : Relese 时,依然会生成扩展名为.vhost.exe和.pdb文件. 其中.pdb是debug ...
 - unity, 不要试图用rigidbody.Sleep()停止rigidbody
			
如果想让rigidbody pause,用rigidbody.Sleep()是完全错误的办法.因为有很多情况都可能使一个处在sleep的rigidbody唤醒,所以调用rigidbody.Sleep( ...
 - 代码覆盖率?coverage
			
http://www.infoq.com/cn/articles/test-coverage-rate-role 代码覆盖率?coverage http://www.cnblogs.com/cod ...
 - Java创建多线程的三种方法
			
Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
 - [JNA系列]Java调用Delphi编写的Dll之Delphi与JAVA基本数据类型对比
			
Delphi与JAVA基本数据类型对比 类型 Delphi关键字 JAVA关键字 字节 备注 范围 整型 Shortint byte 1 有符号8位 -128..127 Byte 1 无符号8位 0 ...
 - ubuntu MySQL数据库输入中文乱码 解决方案
			
一.登录MySQL查看用SHOW VARIABLES LIKE ‘character%’;下字符集,显示如下:+--------------------------+----------------- ...
 - nodejs对文件进行分页
			
//从文件中提取文件指从x行到y行的内容 //awk -v start=5 -v end=10 -F "\x01" '{if(NR>=start && NR& ...