美文网首页Android开发
自己造轮子,写个下载管理器(三)

自己造轮子,写个下载管理器(三)

作者: 凌音同学 | 来源:发表于2017-01-06 13:26 被阅读0次

    DownloadManager.class

    在完成了编写网络请求、下载进度、线程等关键“零件”过后,我们现在要做的就是把这些零件通过一个核心骨架串联,方便调用。这有点类似Java代理模式一样,需要一个或多个Class带负责这些组件的逻辑运行。现在,我们要编写一个完成这些职责的Class,这里就命名为DownloadManager.java

     /**
     * Created by mid1529 on 2016/12/19.
     * 下载管理方法
     */
    public final class DownloadManager {
        private static final String TAG = "DownloadManager";
        public static final int HANDLER_DOWNLOAD_COMPLETED = 0x234123;
        public static final int HANDLER_DOWNLOAD_FAILED = 0x12345234;
        public static final int HANDLER_DOWNLOAD_PROGRESS = 0x2052344;
        public static final int HANDLER_DOWNLOAD_CALL = 0x4234134;
        @SuppressLint("StaticFieldLeak")
        private static DownloadManager ourInstance = new DownloadManager();
        private SQLiteDao mSQLiteDao = null;
        private ArrayMap<Long, DownloadTask> mDownloadTaskQueue = null; //当前下载任务的集合
        private ArrayMap<Long, Call> mDownloadTaskCallQueue = null;
        private DownloadService.DownloadServiceBinder mDownloadServiceBinder = null;
        private ServiceConnection mServiceConnection = null;
        private Application mApplication = null;
        private DownloadCallback mDownloadCallback = null;
        private DownloadHandler mHandler = null;
        private boolean mIsDeleteHistory = false;
        private ExecutorService mCachedThreadPool = null;
    
    
        public synchronized static DownloadManager getInstance() {
            if (ourInstance == null) {
                synchronized (DownloadManager.class) {
                    ourInstance = new DownloadManager();
                }
            }
            return ourInstance;
        }
    
        private DownloadManager() {
            mDownloadTaskQueue = new ArrayMap<>();
            mDownloadTaskCallQueue = new ArrayMap<>();
        }
    
        /**
         * 在Application中调用此方法初始化
         * 只有调用这方法后才可以正常运行,否则会报错
         *
         * @param app Application
         * @return DownloadManager实例
         */
        public synchronized DownloadManager startService(Application app) {
            if (mServiceConnection != null && mApplication != null)
                return ourInstance;
            mHandler = new DownloadHandler();
            mCachedThreadPool = Executors.newCachedThreadPool();
            mApplication = app;
            Intent intent = new Intent(app, DownloadService.class);
            mServiceConnection = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    try {
                        mDownloadServiceBinder = (DownloadService.DownloadServiceBinder) iBinder;
                        mDownloadServiceBinder.setHandler(mHandler);
                    } catch (Exception e) {
                        e.printStackTrace();
                        onDestory();
                    }
                }
    
                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                    onDestory();
                }
            };
            app.startService(intent);
            app.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
            mSQLiteDao = new SQLiteDao(app);
            return ourInstance;
        }
    
        /**
         * 添加到下载队列,阻塞线程,建议到子线程操作
         * 如果任务已存在队列或者任务的url、filename不合法,也会添加错误
         *
         * @param task 下载任务
         * @return 是否添加成功
         */
        public boolean addTaskToQueue(DownloadTask task) {
            if (!isDownloadTaskLegal(task)) //检测下载任务的参数是否合法
                return false;
            checkInited();
            DownloadTask query = mSQLiteDao.queryTask(task);
            task.setPause(false);
            if (query == null) {//不存在表中,执行插入操作,加入下载列表
                mSQLiteDao.insertDownloadTask(task);
                query = mSQLiteDao.queryTask(task);
                task.setTaskId(query.getTaskId());
                mDownloadTaskQueue.put(query.getTaskId(), query);
                mDownloadServiceBinder.startDownload(task);
            } else {
                task.setTaskId(query.getTaskId());
                if (mDownloadTaskQueue.get(task.getTaskId()) == null) {
                    task.setPause(false);
                    mDownloadTaskQueue.put(task.getTaskId(), task);
                    mDownloadServiceBinder.startDownload(task);
                }
            }
            return true;
        }
    
        /**
         * 验证下载任务是否合法
         *
         * @param downloadTask 下载任务
         * @return 是否合法
         */
        @SuppressWarnings("RedundantIfStatement")
        private boolean isDownloadTaskLegal(DownloadTask downloadTask) {
            if (TextUtils.isEmpty(downloadTask.getFileName()))
                return false;
            if (TextUtils.isEmpty(downloadTask.getUrl()))
                return false;
            if (HttpUrl.parse(downloadTask.getUrl()) == null)
                return false;
            return true;
        }
    
        /**
         * 暂停下载,并将下载任务从容器中移除
         *
         * @param task 下载的任务
         */
        public void pauseDownload(DownloadTask task) {
            task.setPause(true);
            removeTaskFromQueue(task, false);
        }
    
    
        /**
         * 从下载列表中移除下载任务
         *
         * @param downloadTask      下载任务
         * @param isDeleteDbHistory 是否删除对应任务在数据库中的记录
         */
        private void removeTaskFromQueue(final DownloadTask downloadTask, boolean isDeleteDbHistory) {
            if (downloadTask.getTaskId() == null)
                return;
            DownloadTask task = mDownloadTaskQueue.get(downloadTask.getTaskId());
            if (task != null) {
                mDownloadTaskQueue.remove(task.getTaskId());
            }
            Call call = mDownloadTaskCallQueue.get(downloadTask.getTaskId());
            if (call != null) {
                call.cancel();
            }
            if (isDeleteDbHistory) { //不需要删除
                //noinspection ConstantConditions
                mCachedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        mSQLiteDao.deleteDownloadTaskByKey(downloadTask.getTaskId());
                    }
                });
            }
        }
    
        private void addCallToQueue(Long taskId, Call call) {
            mDownloadTaskCallQueue.put(taskId, call);
        }
    
        /**
         * 停止下载服务,将会停止所有,且清空下载队列,但不会删除数据库中下载记录
         */
        public void stopService() {
            checkInited();
            mDownloadTaskQueue.clear();
            removeDownloadCallback();
            if (mApplication == null)
                return;
            mApplication.stopService(new Intent(mApplication, DownloadService.class));
            mApplication.unbindService(mServiceConnection);
        }
    
        /**
         * 销毁
         * 清空Handler中的消息
         * 清空用到的容器
         * 将对象置为null
         * 尽量减少内存泄漏的可能
         */
        private void onDestory() {
            mHandler.removeCallbacksAndMessages(null);
            mSQLiteDao = null;
            mDownloadTaskQueue.clear(); //当前下载任务的集合
            mDownloadTaskCallQueue.clear();
            mDownloadServiceBinder = null;
            mServiceConnection = null;
            mCachedThreadPool.shutdown();
            mCachedThreadPool = null;
            mApplication = null;
        }
    
        /**
         * 检查是否已经调用start()方法初始化,没有则直接抛异常
         */
        private void checkInited() {
            if (mDownloadServiceBinder == null) {
                throw new NullPointerException("Do you remember invok \"DownloadManager.getInstance().start()\" in your Application.class? ");
            }
        }
    
        /**
         * @return 返回Dao
         */
        public List<DownloadTask> getDownloadTask() {
            checkInited();
            return mSQLiteDao.queryAllTask();
        }
    
        /**
         * 设置下载回调监听
         *
         * @param callback 回调
         */
        public void setDownloadCallback(DownloadCallback callback) {
            this.mDownloadCallback = callback;
        }
    
        /**
         * 移除监听
         */
        public void removeDownloadCallback() {
            this.mDownloadCallback = null;
        }
    
        /**
         * 更新下载任务到数据库
         *
         * @param downloadTask 下载任务
         */
        private void updateDownloadTask(final DownloadTask downloadTask) {
            mCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    mSQLiteDao.updateDownloadTask(downloadTask);
                }
            });
        }
    
        /**
         * 清空下载的信息
         *
         * @param downloadTask 下载任务
         */
        private void cleanDownloadInfo(final DownloadTask downloadTask) {
            mCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    removeTaskFromQueue(downloadTask, mIsDeleteHistory);
                }
            });
    
        }
    
        /**
         * 是否在下载失败或完成后,删除下载任务
         *
         * @return 是否删除
         */
        public boolean isDeleteHistory() {
            return mIsDeleteHistory;
        }
    
        /**
         * 设置是否删除下载任务的记录
         *
         * @param deleteHistory 是否删除
         */
        public void setDeleteHistory(boolean deleteHistory) {
            mIsDeleteHistory = deleteHistory;
        }
    
        /**
         * Handler负责处理进度传递消息等
         */
        @SuppressWarnings("unchecked")
        private static class DownloadHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case HANDLER_DOWNLOAD_PROGRESS:
                        getInstance().updateDownloadTask((DownloadTask) msg.obj);
                        getInstance().mDownloadCallback.onProgress((DownloadTask) msg.obj);
                        break;
                    case HANDLER_DOWNLOAD_COMPLETED:
                        getInstance().cleanDownloadInfo((DownloadTask) msg.obj);
                        getInstance().mDownloadCallback.onComplete((DownloadTask) msg.obj);
                        break;
                    case HANDLER_DOWNLOAD_FAILED:
                        getInstance().cleanDownloadInfo((DownloadTask) msg.obj);
                        getInstance().mDownloadCallback.onDownloadFaild((DownloadTask) msg.obj);
                        break;
                    case HANDLER_DOWNLOAD_CALL:
                        if (msg.obj instanceof ArrayMap) {
                            ArrayMap<Long, Call> map = (ArrayMap<Long, Call>) msg.obj;
                            if (map.size() != 0) {
                                getInstance().addCallToQueue(map.keyAt(0), map.get(map.keyAt(0)));
                            }
                        }
                        break;
                }
            }
        }
    
    }
    

    典型的,因为一个App中只需要一个下载管理实例,所以我们控制只能用单例模式获取DownloadManager实例,然后进行通信。有兴趣的参考注释阅读。

    总结

    这个项目中,在提取网络Header中的数据时,采用正则表达式提取,比如Range,这种做法总感觉不科学,但不知道有什么方法获取到,这里就谦虚谦虚,谁能告知下科学方法。
    整体思路就是这样,现在只是V0.2版本,很多地方不是很严谨,很多地方偷懒,过两天再改善吧。

    流程图及整体Project周日会放到GitHub中,有兴趣的可以看看

    到这里算是结束了这为期三天的水文,能静下来写点东西还真不容易,好好坚持吧。

    Lolipop

    相关文章

      网友评论

        本文标题:自己造轮子,写个下载管理器(三)

        本文链接:https://www.haomeiwen.com/subject/cdntbttx.html