Android实现简易轻量下载器:单线程任务队列
2016-02-23 11:40
351 查看
最近的项目是一个与音乐相关的App,其中有一个功能:收藏喜欢的歌曲,在wifi的环境下自动下载。
考虑到音乐歌曲都是3、4Mb的小文件,断点下载的功能便不需要了。因此只需要实现一个特别轻量、简单的下载管理类,进行管理即可。
最初的思路便是任务队列,单线程顺序执行,一个文件接着一个文件进行下载。
之前看过AsyncTask的部分源码,其设计与我的想法类似,于是便借鉴着AsyncTask的源码,实现了一个特别简单、轻量的下载管理类。
源码如下:
我们看到
这里我前三个参数传的都是1,既是最多只有1个线程。sPoolWorkQueue参数则是一个容量为128的任务队列,既最多能存放128个任务。
下面我们看到
考虑到音乐歌曲都是3、4Mb的小文件,断点下载的功能便不需要了。因此只需要实现一个特别轻量、简单的下载管理类,进行管理即可。
最初的思路便是任务队列,单线程顺序执行,一个文件接着一个文件进行下载。
之前看过AsyncTask的部分源码,其设计与我的想法类似,于是便借鉴着AsyncTask的源码,实现了一个特别简单、轻量的下载管理类。
源码如下:
public class MyDownloadManager { private static final String TAG = "MyDownloadManager"; private File downloadDir; // 文件保存路径 private static MyDownloadManager instance; // 单例 // 单线程任务队列 public static Executor executor; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "MyDownloadManager #" + mCount.getAndIncrement()); } }; private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(128); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); private MyDownloadManager() { // 初始化下载路径 downloadDir = new File(AndroidCacheUtils.getCacheDirFile(MiaApplication.getInstance()), "download"); if (!downloadDir.exists()) { downloadDir.mkdirs(); } executor = new SerialExecutor(); } /** * 顺序执行下载任务 */ private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } } /** * 获取单例对象 * * @return */ public static MyDownloadManager getInstance() { if (instance == null) { instance = new MyDownloadManager(); } return instance; } /** * 添加下载任务 * * @param path */ public void addDownloadTask(final String path) { executor.execute(new Runnable() { @Override public void run() { download(path); } }); } /** * 下载文件 * * @param path */ private void download(String path) { String fileName = AndroidMD5.MD5(path); File savePath = new File(downloadDir, fileName); // 下载文件路径 File finallyPath = new File(downloadDir, fileName + ".mp3"); // 下载完成后加入.mp3后缀 if (finallyPath.exists()) { // 文件存在则已下载 Log.i(TAG, "file is existed"); return; } if (AndroidNetWorkUtils.isWifiDataEnable(MiaApplication.getInstance())) { // 如果是Wifi则开始下载 if (savePath.exists() && savePath.delete()) { // 如果之前存在文件,证明没有下载完成,删掉重新创建 savePath = new File(downloadDir, fileName); } Log.i(TAG, "download start"); try { byte[] bs = new byte[1024]; int len; URL url = new URL(path); InputStream is = url.openStream(); OutputStream os = new FileOutputStream(savePath); while ((len = is.read(bs)) != -1) { os.write(bs, 0, len); } os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } if (savePath.renameTo(finallyPath)) { // 下载完成后重命名为.mp3文件 Log.i(TAG, "download end"); EventBus.getDefault().post(new DownloadDoneEvent(path)); } } else { // 不是wifi则不下载 Log.i(TAG, "not wifi net, stop download"); } } /** * 添加删除任务 * * @param path */ public void addDeleteTask(final String path) { executor.execute(new Runnable() { @Override public void run() { delete(path); } }); } /** * 删除本地文件 * * @param path */ private void delete(String path) { String fileName = AndroidMD5.MD5(path); File savePath = new File(downloadDir, fileName + ".mp3"); Log.i(TAG, savePath.getPath()); if (savePath.exists()) { if (savePath.delete()) { Log.i(TAG, "file is deleted"); } } } /** * 返回下载路径 * * @return */ public File getDownloadDir() { return downloadDir; } }
我们看到
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1, 1,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);,这句代码便是创建一个线程池。其方法源码及参数说明:
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters and default rejected execution handler. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
这里我前三个参数传的都是1,既是最多只有1个线程。sPoolWorkQueue参数则是一个容量为128的任务队列,既最多能存放128个任务。
下面我们看到
SerialExecutor的代码,它有一个Runnable队列mTasks ,不断的接受Runnable对象,并通过poll操作,每次取出顶部的Runnable进行执行。结合创建的单一线程池,便实现了我需要的简易、轻量的下载器。
相关文章推荐
- EditText添加android:imeOptions="actionSearch"弹出的键盘中带有搜索按钮
- [Android Tips] 17. 查看 APK 签名信息
- Android:onNewIntent()触发机制及注意事项
- Android M 新的运行时权限开发者需要知道的一切
- 设计模式ForAndroid
- Android&java的成长之路之七(2048小游戏③)
- 简单易懂的Android --NDK环境搭建>基础使用过程
- android布局
- Android 开发小提示集合
- Android 自定义漂亮的圆形进度条
- 如何在mac本上安装android sdk 避免被墙
- android限制edittext最大行数的方法
- Android学习笔记day4
- Android 通过JNI实现守护进程,使得Service服务不被杀死
- android shape的使用
- android 常见命令总结
- Java/Android常用工具类
- Android性能优化之Bitmap的内存优化
- Android NDK隐藏jni动态库的内部符号表
- Android 安全机制一:allowBackup安全风险描述