DownloadManager - Part1

原文地址:https://blog.stylingandroid.com/downloadmanager-part-1/

在App中一个相当普遍的操作就是下载内容到我们的设备中,虽然通过网络下载所需要的内容是很简单的,但是还是有另外一种构建方法,如果下载下来的内容可以与其他的应用共享或者存储到设备上一个公共的位置的话将会是非常有用的,在这一系列文章中,将看一下DownloadManager,他是一个易于使用的API,并且能够解决很多常见的问题。

在我们深入了解之前,先提一下,DownloadManager在API 9以来就一直存在,虽然从那之后有一些小的调整,但是大体上已经是相当稳定的了。

现在创建个简单的Activity来包含我们的UI,他包含一个Button,当点击的时候,可以下载一个PDF文件。

重要的一点必须说明的是有一个Downloader对象包含所有的DownloadManager的逻辑,并且当下载成功的时候里面的fileDownloaded()方法会被回调。还有一个很重要的事情就是我们需要在onDestroy中调用Downloaderunregister()方法。

public class MainActivity extends AppCompatActivity implements Downloader.Listener {
    private static final String URI_STRING = "http://www.cbu.edu.zm/downloads/pdf-sample.pdf";
 
    private Button download;
 
    private Downloader downloader;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        download = (Button) findViewById(R.id.download);
        download.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                downloadOrCancel();
            }
        });
        downloader = Downloader.newInstance(this);
    }
 
    void downloadOrCancel() {
        if (downloader.isDownloading()) {
            cancel();
        } else {
            download();
        }
        updateUi();
    }
 
    private void cancel() {
        downloader.cancel();
    }
 
    private void download() {
        Uri uri = Uri.parse(URI_STRING);
        downloader.download(uri);
    }
 
    @Override
    public void fileDownloaded(Uri uri, String mimeType) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, mimeType);
        startActivity(intent);
        updateUi();
    }
 
    @Override
    public Context getContext() {
        return getApplicationContext();
    }

希望上面的代码是简单明了的,接着我们会继续探索DownloadManager的神奇,并更好的了解在 fileDownloaded中做了什么。

现在让我们深入了解Downloader类,看一下我们该如何使用DownloadManager

class Downloader implements DownloadReceiver.Listener {
    private final Listener listener;
    private final DownloadManager downloadManager;

    private DownloadReceiver receiver = null;

    private long downloadId = -1;

    static Downloader newInstance(Listener listener) {
        Context context = listener.getContext();
        DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
        return new Downloader(downloadManager, listener);
    }

    Downloader(DownloadManager downloadManager, Listener listener) {
        this.downloadManager = downloadManager;
        this.listener = listener;
    }

    void download(Uri uri) {
        if (!isDownloading()) {
            register();
            DownloadManager.Request request = new DownloadManager.Request(uri);
            downloadId = downloadManager.enqueue(request);
        }
    }

    boolean isDownloading() {
        return downloadId >= 0;
    }

    void register() {
        if (receiver == null && isDownloading()) {
            receiver = new DownloadReceiver(this);
            receiver.register(listener.getContext());
        }
    }
    @Override
    public void downloadComplete(long completedDownloadId) {
        if (downloadId == completedDownloadId) {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(downloadId);
            downloadId = -1;
            unregister();
            Cursor cursor = downloadManager.query(query);
            while (cursor.moveToNext()) {
                getFileInfo(cursor);
            }
            cursor.close();
        }
    }

    void unregister() {
        if (receiver != null) {
            receiver.unregister(listener.getContext());
        }
        receiver = null;
    }

    private void getFileInfo(Cursor cursor) {
        int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        if (status == DownloadManager.STATUS_SUCCESSFUL) {
            Long id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
            Uri uri = downloadManager.getUriForDownloadedFile(id);
            String mimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
            listener.fileDownloaded(uri, mimeType);
        }
    }

    void cancel() {
        if (isDownloading()) {
            downloadManager.remove(downloadId);
            downloadId = -1;
            unregister();
        }
    }

    interface Listener {
        void fileDownloaded(Uri uri, String mimeType);
        Context getContext();
    }
}

可以通过调用getSystemService(Context.DOWNLOAD_SERVICE)来获取一个DownloadManager的实例。

初始化一个简单的下载并且在download()方法中执行,我们首先注册了一个BroadcastReceiver用来处理DownloadManager一旦下载成功的回调,最后我们通过Uri构造一个DownloadManager.Request,最后,将下载请求加入到DownloadManager中,同时返回一个唯一的ID,用来标识我们此次的下载请求。

我们可以调用cancel()方法来取消下载请求并且使用ID将此次下载从DownloadManager中移除掉。

我们在Activity中onDestroy()中调用的unregister()方法使用来取消注册BroadcastReceiver的。很重要的一点事,我们不能保证在退出应用程序的时候能够下载完成,这也是使用DownloadManager的好处之一,我们不需要创建后台服务来执行下载任务,DownloadManager已经为我们做过了,在Activity中的onDestroy取消下载视为了确保,当App在未激活的状况下下载完成我们不用去尝试做任何事情。

downloadComplete() 将在下载完成时由BroadcastReceiver调用,首先会检查他是否匹配我们的下载Id,如果一致的话,我们会查询DownloadManager然后获取当前的下载信息,如果是多个任务同时下载的话,那么我们需要定义多个下载ID在查询中,然后Cursor会返回多条数据,在上面的例子中,就下载了一个任务,但是那些代码同样适用于多个任务的情况。

首先我们需要提取下载状态的字段,用来指示下载的状态,在我们的情况下,只有在下载完成时会被触发,所以我们只需要检查下载是否成功。然而在一个完整的实现中,恢复之前启动应用的下载状态将会非常有用。

一旦下载成功的时候,我们可以提取内容的本地Uri(我们下载所存储的位置)和MIME 类型,转换之后返回给我们的监听者。

让我们看一下BroadcastReceiver的代码:

class DownloadReceiver extends BroadcastReceiver {
    private final Listener listener;
 
    DownloadReceiver(Listener listener) {
        this.listener = listener;
    }
 
    @Override
    public void onReceive(Context context, Intent intent) {
        long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        listener.downloadComplete(downloadId);
    }
 
    public void register(Context context) {
        IntentFilter downloadFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        context.registerReceiver(this, downloadFilter);
    }
 
    public void unregister(Context context) {
        context.unregisterReceiver(this);
    }
 
    interface Listener {
        void downloadComplete(long downloadId);
    }
}

这里值得注意的是我们需要注册的操作是DownloadManager.ACTION_DOWNLOAD_COMPLETE,然后将被下载完成的事件唤醒,我们也可以注册其他事件,例如用户点击下载通知事件,可以将用户直接带到应用中来控制或者查看下载的状态,但是通常我们只对完成感兴趣。

还有一个值得注意的是,当我们接收到下载完成的Intent时,可以从Intent extras中获取downloadId。

@Override
public void fileDownloaded(Uri uri, String mimeType) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, mimeType);
        startActivity(intent);
        updateUi();
    }

下一篇文章,我们将会深入的剖析DownloadManager更多强大的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容