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

    我们来看一下这个文件的内容:

    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/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值