文档章节

Android 多线程后台下载,查看下载列表/通知栏

我希冀着
 我希冀着
发布于 2014/08/27 17:17
字数 1724
阅读 283
收藏 0

先看看下面的几张图片

第一张是任务列表,第二是点击下载之后的页面,第三是在通知栏显示的下载进度。可以查看下载任务,又可以在通知栏显示这里面这里边用到了

数据库(sqlite)。把下载信息保存在数据中,然后通过ContentObserver来监听数据库的变化,在thread里面不断的更新数据库中的下载信息,从而ContentObserver

检测到数据库的变化会调用ContentObserver里面的onChange(boolean selfChange)方法。使用ContentObserver又牵扯到了Android的ContentProvider组件,

ContentObserver和ContentProvider的结合完成了数据库监听动作,为什么要用ContentObserver监听数据库而不是直接开启一个线程不断的读取数据里面的下载信息呢?

这里面的效率就不言而喻了。如果从下载开始开启一个线程不停的读取数据库,那过不了多久手机就可以“煎蛋”了,而且里面的许多细节不好处理。把下载拆分为

三部实现可以这样做:

首先,为下载做前期的准备,方便控制下载过程中的“暂停”、“删除”、“回复”操作,可以建立一些下载的标志例如:

/**
 * Created by huangsm.
 * Email:huangsanm@foxmail.com
 */
public class DownloadStatus {

    // 下载失败
    public static final int STATUS_FAILED = -1;

    // 成功
    public static final int STATUS_SUCCESSFUL = 4;

    //下载中
    public static final int STATUS_RUNNING = 1;

    // 暂停
    public static final int STATUS_PAUSED = 2;

    // 继续
    public static final int STATUS_PENDING = 3;

    //delete
    public static final int STATUS_DELETEED = -2;

}

把下载的相关操作都放到Service里面去执行,Service里面的做的事情很简单,这里Service的生命周期之类就不介绍了,在Service里面的onStartCommand方法里面接受点击

下载按钮传递过来的下载参数【下载地址,名称,等等】。


其次,下载的基本参数都准备好了,这个时候就真正实现在的内容了。把下载的内容放到Thread里面来实现,这个时候你考虑Thread会太多降低性能,我们可以开启线程池来管理

这些Thread

//保存下载内容  key:downloadID
    //private Map<Integer, DownloadItem> mDownloadMap;
    //线程池
    private ExecutorService mExecutor;
    private Context mContext;
    private DownloadManager mDownloadManager;

    @Override
    public void onCreate() {
        mContext = this;
        //mDownloadMap = Collections.synchronizedMap(new HashMap<Integer, DownloadItem>());
        mExecutor = Executors.newCachedThreadPool();
        mDownloadManager = new DownloadManager(getContentResolver());
        super.onCreate();
    }

在下载过程中有一个很巧妙的运用就是下载的临时文件,在开启下载的过程中,可以先判断临时文件是否存在,如果存在则继续下载,如果不存在从头开始。下载文件里面继续

下载的时候主要有涉及到要设置http的header部分和RandomAccessFile类,RandomAccessFile类里面有一个方法可以设置下载的节点。下载过程中先判断临时文件是否存在

如果存在先读取临时文件的大小

if (mTempFile.exists()) {
    mTempSize = (int) mTempFile.length();
    request.addHeader("RANGE", "bytes=" + mTempFile.length() + "-");//下载从这个节点开始
    aClient.close();
    aClient = AndroidHttpClient.newInstance("Linux; Android");
    response = aClient.execute(request);
}
long storage = DownloadUtils.getAvailableStorage();
if (totalSize - mTempSize > storage) {
     notifyNotification(NOTIFICATION_STATUS_FLAG_ERROR, "您的手机内存不足", 0, 0);
     return;
}
RandomAccessFile outStream = new RandomAccessFile(mTempFile, "rwd");
outStream.seek(mTempSize);//文件写入也从这个节点开始写入

然后在读取文件的循环里面判断当前下载是否被暂停、删除等等

InputStream is = response.getEntity().getContent();
            byte[] buffer = new byte[BUFFER_SIZE];
            bufferedInputStream = new BufferedInputStream(is, BUFFER_SIZE);
            int b = 0;
            long updateStart = System.currentTimeMillis();
            long updateDelta = 0;
            int progress = mTempSize;
            while (true) {
                //check download status
                if (checkDownloadCancel()) {
                    notifyNotification(NOTIFICATION_STATUS_FLAG_DELETE, "下载已被取消", 0, 0);
                    aClient.close();
                    break;
                }

                if (checkDownloadPause()) {
                    notifyNotification(NOTIFICATION_STATUS_FLAG_DELETE, "暂停下载", 0, 0);
                    aClient.close();
                    break;
                }

                b = bufferedInputStream.read(buffer, 0, BUFFER_SIZE);
                if (b == -1) {
                    break;
                }
                outStream.write(buffer, 0, b);
                progress += b;