-
在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
我们来看一下这个文件的内容:
01.
17package com.android.providers.downloads.ui;
02.
18
03.
19import android.app.Activity;
04.
20import android.content.Intent;
05.
21import android.os.Bundle;
06.
22import android.provider.DocumentsContract;
07.
23
08.
24import com.android.providers.downloads.Constants;
09.
25
10.
26public
class
DownloadList
extends
Activity {
11.
27
@Override
12.
28
public
void
onCreate(Bundle icicle) {
13.
29
super
.onCreate(icicle);
14.
30
15.
31
// Trampoline over to new management UI
16.
32
final
Intent intent =
new
Intent(DocumentsContract.ACTION_MANAGE_ROOT);
17.
33
intent.setData(DocumentsContract.buildRootUri(
18.
34
Constants.STORAGE_AUTHORITY, Constants.STORAGE_ROOT_ID));
19.
35
startActivity(intent);
20.
36
finish();
21.
37
}
22.
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是专门存储状态信息的
01.
223
private
void
buildDefaultState() {
02.
224
mState =
new
State();
03.
225
04.
226
final
Intent intent = getIntent();
05.
227
final
String action = intent.getAction();
06.
228
if
(Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
07.
229
mState.action = ACTION_OPEN;
08.
230
}
else
if
(Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
09.
231
mState.action = ACTION_CREATE;
10.
232
}
else
if
(Intent.ACTION_GET_CONTENT.equals(action)) {
11.
233
mState.action = ACTION_GET_CONTENT;
12.
234
}
else
if
(Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
13.
235
mState.action = ACTION_OPEN_TREE;
14.
236
}
else
if
(DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
15.
237
mState.action = ACTION_MANAGE;
16.
238
}
17.
239
18.
240
if
(mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
19.
241
mState.allowMultiple = intent.getBooleanExtra(
20.
242
Intent.EXTRA_ALLOW_MULTIPLE,
false
);
21.
243
}
22.
244
23.
245
if
(mState.action == ACTION_MANAGE) {
24.
246
mState.acceptMimes =
new
String[] {
'*/*'
};
25.
247
mState.allowMultiple =
true
;
26.
248
}
else
if
(intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
27.
249
mState.acceptMimes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES);
28.
250
}
else
{
29.
251
mState.acceptMimes =
new
String[] { intent.getType() };
30.
252
}
31.
253
32.
254
mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY,
false
);
33.
255
mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED,
false
);
34.
256
mState.showAdvanced = mState.forceAdvanced
35.
257
| LocalPreferences.getDisplayAdvancedDevices(
this
);
36.
258
37.
259
if
(mState.action == ACTION_MANAGE) {
38.
260
mState.showSize =
true
;
39.
261
}
else
{
40.
262
mState.showSize = LocalPreferences.getDisplayFileSize(
this
);
41.
263
}
42.
264
}
由于前面传入的action为“DocumentsContract.ACTION_MANAGE_ROOT”,这里会走236行将mState.action 设置为 ACTION_MANAGE
在Sate类中的restored变量初始值为false,所以在onCreate方法中会走下面这段代码
01.
211
if
(!mState.restored) {
02.
212
if
(mState.action == ACTION_MANAGE) {
03.
213
final
Uri rootUri = getIntent().getData();
04.
214
new
RestoreRootTask(rootUri).executeOnExecutor(getCurrentExecutor());
05.
215
}
else
{
06.
216
new
RestoreStackTask().execute();
07.
217
}
08.
218
}
else
{
09.
219
onCurrentDirectoryChanged(ANIM_NONE);
10.
220
}
通过调用RestoreRootTask来将当面信息保存下来,接着调用onRootPicked来对需要显示的内容做相应判断。
在onRootPicked中又会启动另一个异步任务PickRootTask,它主要作用是通过intent中的data信息来构建DocumentInfo的对象,该对象中主要保存一下字段
01.
56
public
String authority;
02.
57
public
String documentId;
03.
58
public
String mimeType;
04.
59
public
String displayName;
05.
60
public
long
lastModified;
06.
61
public
int
flags;
07.
62
public
String summary;
08.
63
public
long
size;
09.
64
public
int
icon;
这里的authority现在的值为'com.android.providers.downloads.documents',documentId的值为'downloads'
获得了以上信息后,PickRootTask的任务差不多就完成了,接着它会调用onCurrentDirectoryChanged来告诉DocumentsUI,“有人要显示所有下载的信息,我这边的信息处理完了你可以去查询和显示下载信息了”
查询和显示对应的数据流程如下
首先在onCreateView方法中初始化界面布局,接着在onActivityCreated中初始化Adapter和异步数据加载器,最后将从数据库取出来的数据与Adapter进行绑定。
这里我们重点看异步查询数据部分
01.
mCallbacks =
new
LoaderCallbacks<DirectoryResult>() {
02.
262
@Override
03.
263
public
Loader<DirectoryResult> onCreateLoader(
int
id, Bundle args) {
04.
264
final
String query = getArguments().getString(EXTRA_QUERY);
05.
265
06.
266
Uri contentsUri;
07.
267
switch
(mType) {
08.
268
case
TYPE_NORMAL:
09.
269
contentsUri = DocumentsContract.buildChildDocumentsUri(
10.
270
doc.authority, doc.documentId);
11.
271
if
(state.action == ACTION_MANAGE) {
12.
272
contentsUri = DocumentsContract.setManageMode(contentsUri);
13.
273
}
14.
274
return
new
DirectoryLoader(
15.
275
context, mType, root, doc, contentsUri, state.userSortOrder);
16.
276
//部分代码省略
17.
289
}
18.
290
}
19.
291
20.
292
@Override
21.
293
public
void
onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
22.
294
if
(!isAdded())
return
;
23.
295
24.
296
mAdapter.swapResult(result);
25.
297
26.
//部分代码省略
27.
321
28.
322
mLastSortOrder = state.derivedSortOrder;
29.
323
}
30.
324
31.
325
@Override
32.
326
public
void
onLoaderReset(Loader<DirectoryResult> loader) {
33.
327
mAdapter.swapResult(
null
);
34.
328
}
35.
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部分
最新推荐文章于 2024-03-27 09:36:26 发布