精华内容
下载资源
问答
  • android 多线程下载框架

    千次阅读 2016-04-14 14:52:35
    推荐一个非常不错的断点续传 多线程下载框架 https://github.com/lingochamp/FileDownloader/blob/master/README-zh.md

    推荐一个非常不错的断点续传 多线程下载框架


    https://github.com/lingochamp/FileDownloader/blob/master/README-zh.md





    展开全文
  • android的一个下载框架

    千次阅读 2017-06-05 17:18:00
    Aria项目源于15年工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标

    http://www.jianshu.com/p/ba32dc499db3

    https://github.com/AriaLyy/Aria

    本文主要介绍开源项目Aria的使用。
    Aria项目源于15年工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标了。

    以下为Aria的使用示例,使用Aria开发图片中的所有功能,只需要很短的时间,很少的代码。


    Aria示例

    编写布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent">
    
      <com.arialyy.simple.widget.HorizontalProgressBarWithNumber
          android:id="@+id/progressBar"
          android:layout_width="wrap_content"
          android:layout_height="20dp"
          android:layout_alignParentLeft="true"
          android:layout_alignParentStart="true"
          android:layout_alignParentTop="true"
          android:layout_margin="16dp"
          android:layout_toLeftOf="@+id/size"
          android:max="100"
          style="?android:attr/progressBarStyleHorizontal"/>
    
      <TextView
          android:id="@+id/size"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignParentRight="true"
          android:layout_alignTop="@+id/progressBar"
          android:layout_marginRight="16dp"
          android:text="0mb"
          android:textSize="16sp"/>
    
      <LinearLayout
          android:id="@+id/handle_bar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_below="@+id/progressBar"
          android:orientation="horizontal">
    
        <TextView
            android:id="@+id/speed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:text="0kb/s"
            android:textColor="@android:color/black"/>
    
        <Button
            android:id="@+id/start"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="开始"
            style="?buttonBarButtonStyle"/>
    
        <Button
            android:id="@+id/stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="暂停"
            style="?buttonBarButtonStyle"/>
    
        <Button
            android:id="@+id/cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="删除任务"
            style="?buttonBarButtonStyle"/>
      </LinearLayout>
    
    </RelativeLayout>

    调用Aria的api进行下载

    @OnClick({ R.id.start, R.id.stop, R.id.cancel }) public void onClick(View view) {
       switch (view.getId()) {
         case R.id.start:
           Aria.download(this)
               .load(DOWNLOAD_URL)
               .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/test.apk")
               .start();
           break;
         case R.id.stop:
           Aria.download(this).load(DOWNLOAD_URL).pause();
           break;
         case R.id.cancel:
           Aria.download(this).load(DOWNLOAD_URL).cancel();
           break;
       }
     }

    在Aria的下载模块中,下载链接为下载任务的唯一标识符,控制一个任务的开始、暂停、恢复、取消下载等操作都需要DOWNLOAD_URL支持。
    另外,在Aira中,可以调用start()方法开始下载或恢复下载。
    为了方便起见,你也可以调用Aria.download(this).load(DOWNLOAD_URL).resume();实现恢复下载。

    Aria 提供了大量的API,除了以上几个常用的API外,你还可以Aria Api;

    下载事件的获取

    在上文中,我们已经实现在android中下载文件的操作,是的,使用Aria,一行代码你就能实现复杂的断点续传功能。
    除了正常的文件需求外,有的时候,我们还需要获取文件的下载状态用来更新界面的显示。
    Aria中,使用Aria的事件监听器,你能很容易的获取到你想要的很多事件,如:

    • 暂停、恢复、完成、失败、取消下载等状态
    • 一句代码获取当前任务的下载速度
    • 一句代码获取当前任务的进度的百分比
    • 一句代码获取当前任务的的文件大小
      ...

      private class MySchedulerListener extends Aria.DownloadSchedulerListener {
      
        @Override public void onTaskStart(DownloadTask task) {
          mSize.setText(task.getConvertFileSize());
        }
      
        @Override public void onTaskStop(DownloadTask task) {
          Toast.makeText(MainActivity.this, "停止下载", Toast.LENGTH_SHORT).show();
        }
      
        @Override public void onTaskCancel(DownloadTask task) {
          Toast.makeText(MainActivity.this, "取消下载", Toast.LENGTH_SHORT).show();
        }
      
        @Override public void onTaskFail(DownloadTask task) {
          Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
        }
      
        @Override public void onTaskComplete(DownloadTask task) {
          Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
        }
      
        @Override public void onTaskRunning(DownloadTask task) {
          //使用转换单位后的速度,需要在aria_config.xml配置文件中将单位转换开关打开
          //https://github.com/AriaLyy/Aria#配置文件设置参数
          mSpeed.setText(task.getConvertSpeed());
          mPb.setProgress(task.getPercent());
        }
      }

      上文只列出了常见的下载事件,更多的下载状态事件见Aria下载事件监听器说明

    在Aria中注册新创建的事件监听器

    上文中,已经创建完了事件监听器,这时,事件监听器还没能生效,你还需要将事件监听器注册到Aria中

    @Override protected void onResume() {
        super.onResume();
        Aria.download(this).addSchedulerListener(new MySchedulerListener());
      }

    最终效果


    最终效果

    final

    以上,便是使用Aria进行文件下载的所有代码了,总的来说,Aria真的很简单。
    很少的代码,你就能实现复杂的文件多线程断点续传功能,除此之外,Aria还支持多线程的配置,使用多少线程下载一个文件,由你决定,具体见多线程配置
    Aria默认支持任务自动调度,从此,你再也不需要关心任务的暂停、恢复、完成、失败、取消等状态的调度。

    https://github.com/AriaLyy/Aria
    展开全文
  • 一、什么是多任务下载框架多任务框架就是指,同一时间内,支持多个资源下载。支持下载队列、进度更新、下载取消暂停等。包括:网络下载请求,下载任务执行、下载任务调度、UI进度更新、任务状态变化、文件的存储。二...

    多任务下载在android app中很多应用场景,比如应用市场下载app,图书下载、音乐下载、电影下载等资源类型下载。


    一、什么是多任务下载框架


    多任务框架就是指,同一时间内,支持多个资源下载。支持下载队列、进度更新、下载取消暂停等。

    包括:网络下载请求,下载任务执行、下载任务调度、UI进度更新、任务状态变化、文件的存储。


    二、框架流程


    三、框架代码:



    下面着重分析下DownloadTask和TaskDispatcher部分代码:


    四、任劳任怨的下载器--DownloadTask


    DownloadTask实现了Runnable接口,定义了任务状态和网络service。具体参考代码:

    /**
     * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
     * <p>
     * HappyBaby
     * <p>
     * 作者:Jacky.Ao on 2018/2/23 17:08
     * <p>
     * 邮箱: jiazhi.ao@gmail.com
     */
    
    public class DownloadTask implements Runnable, ProgressListener  {
        //更新任务进度消息
        private static final int UPDATE_PROGRESS_ID = 0x100;
        //下载网络服务
        private APIService.DownloadApiService downloadApiService;
        //上传网络服务
        private APIService.UploadApiService uploadApiService;
        //下载任务状态
        private STATE state;
        //下载实体类,使用object基类,方便统一获取
        private Object downloadObject;
        //网络服务请求参数列表
        private List<RequestParameter> parameterList;
        //网络下载请求对象
        private Call<File> downloadCall;
        //网络上传请求对象
        private Call<BaseEntity> uploadCall;
        //下载保存文件对象
        private File downloadFile = null;
        //下载任务进度监听器
        private OnProgressListener onProgressListener;
        private DownloadTask mySelf;
        //是否是下载,区分当前任务是下载还是上传
        private boolean isDownload;
    
        @Override
        public void run() {
            start();
        }
    
        @Override
        public void onProgress(long addedBytes, long contentLenght, boolean done) {
            sendUpdateProgressMessage(addedBytes, contentLenght, false);
        }
    
        public enum STATE {
            IDLE,
            PENDING,
            LOADING,
            FAILED,
            FINISHED,
            UNKNOWN,
        }
    
        private void sendUpdateProgressMessage(long addedBytes, long contentLenght, boolean done) {
            Message message = handler.obtainMessage(UPDATE_PROGRESS_ID);
            message.obj = done;
            message.arg1 = (int) addedBytes;
            message.arg2 = (int) contentLenght;
            handler.sendMessage(message);
        }
    
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == UPDATE_PROGRESS_ID) {
                    if (onProgressListener != null) {
                        onProgressListener.onProgress(msg.arg1, msg.arg2, (Boolean) msg.obj);
                    }
                }
            }
        };
    
        public DownloadTask(final Object object, List<RequestParameter> list, final boolean download) {
            downloadObject = object;
            parameterList = list;
            isDownload = download;
            if (isDownload) {
                downloadApiService = HttpApiService.getDownloadApiService(this);
            } else {
                uploadApiService = HttpApiService.getUploadApiService(this);
            }
            state = STATE.IDLE;
            mySelf = this;
        }
    
        public void start() {
            if (state == STATE.LOADING) {
                return;
            }
            state = STATE.LOADING;
            if (isDownload) {
                download();
            } else {
                upload();
            }
        }
    
        private void download() {
            if (parameterList != null && parameterList.size() > 1 && downloadApiService != null) {
                //change state pending or idle to loading, notify ui to update.
                sendUpdateProgressMessage(0, 0, false);
                String downloadFilename = parameterList.get(0).getValue();
                String saveFilename = parameterList.get(1).getValue();
                downloadCall = downloadApiService.httpDownloadFile(downloadFilename, saveFilename);
                downloadCall.enqueue(new Callback<File>() {
                    @Override
                    public void onResponse(Call<File> call, Response<File> response) {
                        Log.i(response.toString());
                        if (response.code() == 200) {
                            mySelf.downloadFile = response.body();
                            if (mySelf.downloadFile != null && !mySelf.downloadFile.getPath().endsWith(".tmp")) {
                                sendUpdateProgressMessage(100, 100, true);
                                mySelf.state = STATE.FINISHED;
                                TaskDispatcher.getInstance().finished(mySelf);
                            } else {
                                mySelf.state = STATE.FAILED;
                                sendUpdateProgressMessage(0, 0, false);
                            }
                        }
                    }
    
                    @Override
                    public void onFailure(Call<File> call, Throwable t) {
                        mySelf.state = STATE.FAILED;
                        sendUpdateProgressMessage(0, 0, false);
                    }
                });
            }
        }
    
        private void upload() {
            if (parameterList != null && parameterList.size() > 1 && uploadApiService != null) {
                File file = new File(parameterList.get(0).getValue());
                String uid = parameterList.get(1).getValue();
                RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
                MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);
                uploadCall = uploadApiService.upLoad(uid, body); // "34"
                uploadCall.enqueue(new Callback<BaseEntity>() {
                    @Override
                    public void onResponse(Call<BaseEntity> call, Response<BaseEntity> response) {
                        Log.i(response.body().toString());
                        if (response.code() == 200 && response.body().status.equals("2000")) {
                            sendUpdateProgressMessage(100, 100, true);
                            mySelf.state = STATE.FINISHED;
                            TaskDispatcher.getInstance().finished(mySelf);
                        } else {
                            mySelf.state = STATE.FAILED;
                            sendUpdateProgressMessage(0, 0, false);
                        }
                    }
    
                    @Override
                    public void onFailure(Call<BaseEntity> call, Throwable t) {
                        Log.e(t.getMessage());
                    }
                });
            }
        }
    
        public void cancel() {
            if (downloadCall != null) {
                downloadCall.cancel();
            }
    
            handler.removeMessages(UPDATE_PROGRESS_ID);
        }
    
        public void setState(final STATE state) {
            this.state = state;
        }
    
        public STATE getState() {
            return state;
        }
    
        public Object getDownloadObject() {
            return downloadObject;
        }
    
        public void setDownloadObject(Object downloadObject) {
            this.downloadObject = downloadObject;
        }
    
        public File getDownloadFile() {
            return downloadFile;
        }
    
        public boolean isDownload() {
            return isDownload;
        }
    
        public void setOnProgressListener(final OnProgressListener listener) {
            onProgressListener = listener;
        }
    
        public interface OnProgressListener {
            void onProgress(long addedBytes, long contentLenght, boolean done);
        }
    }
    

    上面省略了一些无关代码,代码写的还是比较简洁,很容易读懂。构造函数中实例了下载服务,并注册了进度更新的监听器,监听器同handler消息更新UI,方便在当前线程更新UI。具体下载参考downloadApiService.httpDownloadFile接口,返回一个Call,下载完成通过OnResponse把保存的文件对象回传回来,保存在downloadFile对象中并修改任务状态为Finished。五、忠于职守的巡查官--任务调度器

    TaskDispatcher主要实现下载任务的调度,具体参考代码:

    /**
     * <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
     * <p>
     * HappyBaby
     * <p>
     * 作者:Jacky.Ao on 2018/2/24 15:55
     * <p>
     * 邮箱: jiazhi.ao@gmail.com
     */
    
    public class TaskDispatcher {
        //最大下载任务数量
        private static final int DOWNLOAD_MAX = 3;
        //下载任务线程池
        private ExecutorService executorService;
        //正在下载的任务队列
        private List<DownloadTask> queueTaskList = Collections.synchronizedList(new ArrayList<>());
        //已经完成下载任务队列
        private List<DownloadTask> downloadedList = Collections.synchronizedList(new ArrayList<>());
        //上传任务队列
        private List<DownloadTask> uploadList = Collections.synchronizedList(new ArrayList<>());
        //单例对象
        private static TaskDispatcher instance;
        //任务是否中断
        private boolean taskAbort;
    
        private TaskDispatcher() {
    
        }
        /**
         *线程安全单例模式
         */
        public static TaskDispatcher getInstance() {
            if (instance == null) {
                synchronized (TaskDispatcher.class) {
                    if (instance == null) {
                        instance = new TaskDispatcher();
                    }
                }
            }
            return instance;
        }
    
        /**
         * 初始化线程池
         */
        private ExecutorService getExecutorService() {
            if (executorService == null) {
                executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                        60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
                        threadFactory("happybaby download dispatcher", false));
            }
            return executorService;
        }
    
        /**
         * 任务入列下载
         */
        public synchronized boolean enqueue(DownloadTask task) {
            if (queueTaskList.contains(task) || uploadList.contains(task)) {
                return false;
            }
            if (task != null && task.isDownload()) {
                if (queueTaskList.size() < DOWNLOAD_MAX) {
                    queueTaskList.add(task);
                    getExecutorService().execute(task);
                } else {
                    task.setState(DownloadTask.STATE.PENDING);
                    queueTaskList.add(task);
                }
                return true;
            } else {
                if (uploadList.size() < DOWNLOAD_MAX) {
                    uploadList.add(task);
                    getExecutorService().execute(task);
                } else {
                    task.setState(DownloadTask.STATE.PENDING);
                    uploadList.add(task);
                }
                return true;
            }
        }
    
        /**
         * 任务下载完成
         */
        public synchronized void finished(DownloadTask task) {
            if (task != null && task.getState() == DownloadTask.STATE.FINISHED) {
                if (task.isDownload()) {
                    if (queueTaskList.remove(task)) {
                        downloadedList.add(task);
                        promoteSyncTask();
                    }
                } else {
                    uploadList.remove(task);
                    promoteSyncUploadTask();
                }
            }
        }
    
        /**
         * 删除下载任务,是否删除文件
         */
        public synchronized void deleteTask(DownloadTask task, boolean isDeleteFile) {
            if (task != null) {
                if (task.getState() != DownloadTask.STATE.FINISHED) {
                    if (task.isDownload()) {
                        queueTaskList.remove(task);
                        if (task.getState() == DownloadTask.STATE.LOADING) {
                            task.cancel();
                        }
                        promoteSyncTask();
                    } else {
                        uploadList.remove(task);
                        if (task.getState() == DownloadTask.STATE.LOADING) {
                            task.cancel();
                        }
                        promoteSyncUploadTask();
                    }
                    return;
                }
                downloadedList.remove(task);
                if (isDeleteFile) {
                    task.getDownloadFile().delete();
                }
            }
        }
    
        /**
         *失败任务重新下载
         */
        public synchronized void promoteSyncFailedTask() {
            if (taskAbort && queueTaskList.size() > 0) {
                for (Iterator<DownloadTask> it = queueTaskList.iterator(); it.hasNext(); ) {
                    DownloadTask task = it.next();
                    if (task.getState() == DownloadTask.STATE.FAILED) {
                        getExecutorService().execute(task);
                    }
                }
            }
            if (taskAbort && uploadList.size() > 0) {
                for (Iterator<DownloadTask> it = uploadList.iterator(); it.hasNext(); ) {
                    DownloadTask task = it.next();
                    if (task.getState() == DownloadTask.STATE.FAILED) {
                        getExecutorService().execute(task);
                    }
                }
            }
        }
    
        /**
         * 调度上传任务
         */
        private synchronized void promoteSyncUploadTask() {
            for (Iterator<DownloadTask> it = uploadList.iterator(); it.hasNext();) {
                DownloadTask task = it.next();
                if (task.getState() == DownloadTask.STATE.PENDING) {
                    getExecutorService().execute(task);
                    return;
                }
            }
        }
    
        /**
         * 调度pending状态的任务,开始下载
         */
        private synchronized void promoteSyncTask() {
            for (Iterator<DownloadTask> it = queueTaskList.iterator(); it.hasNext();) {
                DownloadTask task = it.next();
                if (task.getState() == DownloadTask.STATE.PENDING) {
                    getExecutorService().execute(task);
                    return;
                }
            }
        }
    
        public List<DownloadTask> getQueueTaskList() {
            return queueTaskList;
        }
    
        public List<DownloadTask> getDownloadedList() {
            return downloadedList;
        }
    
        public List<DownloadTask> getUploadList() {
            return uploadList;
        }
    
        /**
         * 取消所有任务
         */
        public synchronized void cancelAll() {
            for (DownloadTask task : queueTaskList) {
                if (task.getState() == DownloadTask.STATE.LOADING) {
                    task.cancel();
                }
            }
            for (DownloadTask task : uploadList) {
                if (task.getState() == DownloadTask.STATE.LOADING) {
                    task.cancel();
                }
            }
        }
    
        public void setTaskAbort(boolean taskAbort) {
            this.taskAbort = taskAbort;
        }
    
        private ThreadFactory threadFactory(final String name, final boolean daemon) {
            return new ThreadFactory() {
                @Override
                public Thread newThread(@NonNull Runnable r) {
                    Thread thread = new Thread(r, name);
                    thread.setDaemon(daemon);
                    return thread;
                }
            };
        }
    }

    上面是TaskDispatcher部分代码,重点看下enqueue函数,该函数是个入列操作,根据传入的task,    加入下载队列,如果当前下载队列超出最大任务数量,将任务状态修改为pending状态,等待任务    完成即finished接口,将当前下载完成的task添加到下载完成队列,并调用promoteSyncTask接口,    检查是否有pending的任务,如果有则开始任务。

    六、测试示例:

    RequestParameter parameter = new RequestParameter("name", fileName);
    RequestParameter parameter1 = new RequestParameter("savename", filePath + fileName);
    List<RequestParameter> parameterList = new ArrayList<>();
    parameterList.add(parameter);
    parameterList.add(parameter1);
    
    DownloadTask downloadTask = new DownloadTask(musicEntity, parameterList,true);
    downloadTask.setDownloadObject(musicEntity);
    
    downloadTask.setOnProgressListener(new DownloadTask.OnProgressListener() {
        @Override
        public void onProgress(long addedBytes, long contentLenght, boolean done) {
            if (contentLenght > 0) {
                holder.progress.setProgress((int) (1.0f * addedBytes / contentLenght * 100));
            }
            if (done) {
                Log.i(entity.getName() + " finished...");
                notifyItemRemoved(position);
            }
        }
    });
    
    TaskDispatcher.getInstance().enqueue(downloadTask);

    七、效果图



    原创不易,如果您觉得好,可以分享此公众号给你更多的人。




    展开全文
  • 更好的Android多线程下载框架

    千次阅读 2017-03-30 15:28:52
    */概述为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点: 多线程 多任务 断点续传 支持大文件 可以自定义下载数据库
    /**
     * 作者:Pich
     * 原文链接:http://me.woblog.cn/
     * QQ群:129961195
     * Github:https://github.com/lifengsofts
     */

    概述

    为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!

    本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点:

    1. 多线程
    2. 多任务
    3. 断点续传
    4. 支持大文件
    5. 可以自定义下载数据库
    6. 高度可配置,像超时时间这类
    7. 业务数据和下载数据分离

    下面我们在说下该框架能实现那些的应用场景:

    1. 该框架可以很方便的下载单个文件,并且显示各种状态,包括开始下载,下载中,下载失败,删除等状态。
    2. 也可以实现常见的需要下载功能应用,比如:某某手机助手,在该应用内可以说是下载是核心功能,所以对框架的稳定性,代码可靠性,框架扩展性依赖很大,所以该框架真是从这种出发点而生的。通常这类应用的表示形式分三个页面需要用到下载功能,一个列表用来显示来自业务数据的列表,在该列表右边可以点击单个条目,或者多选实现下载,点击每个条目进入详情,同时还有个一个下载管理,包括大概两个界面,正在下载,下载完成的,在这几个界面都需要一个核心的功能就是都可以暂停,恢复,删除并且能显示下载进度。在列表一个最重要的问题就是界面刷新,如果每次更新都刷新整个列表,那么这将是异常灾难,而我们这个框架正好解决了该问题,采用了回调单个条目并更新该条目的进度和状态。

    该项目状态

    该项目的雏形始于14年的公司项目需要用到多线程下载,但当时实现的单线程多任务断点续传,后面不断完善,在这之间遇到过很多坑,也对一个下载框架有了更深的认识,所以在16年又重写了该框架。

    项目的Github地址:https://github.com/lifengsofts/AndroidDownloader

    项目的官网地址:http://i.woblog.cn/AndroidDownloader

    项目还处于发展状态,但已经趋于稳定,并且有一定的编码规范,同时采用了多个开源项目的质量控制方案以保证每次代码提交的可靠性。

    下面上几张框架Demo的截图,这样用户在心中有一个自己的概念,但是推荐各位还是讲Demo下载到本地亲自,运行一下。

    截图

    AndroidDownloader Sample ScreenshotsAndroidDownloader Sample Screenshots

    AndroidDownloader Sample ScreenshotsAndroidDownloader Sample Screenshots

    第一个界面是单独下载一个文件。

    第二个界面是应用中最常用的一个界面,该界面来自业务数据。

    第三个页面是离线管理中的下载中的界面。

    第四个页面是离线管理中的下载完成的界面。

    可以看到他们在每个界面都能暂停下载,继续下载,以及删除,并且都能拿到进度,状态等信息。

    下面就来看看这么强大的下载框架那该如何来使用呢?

    添加权限

    我相信这一步任何一个项目都已经添加了,但是还是不得不提一下。

    该框架需要网络访问权限,如果你是讲文件下载到存储卡,那相应的需要添加存储卡访问权限。

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    配置Service

    因为该框架采用才service中下载一个文件的。这样做的目的是下载任务一般都需要在后头下载,如果在Activity中来做这类任务,我想任何一个新手都知道这样不行。

    <service android:name="cn.woblog.android.downloader.DownloadService">
      <intent-filter>
        <action android:name="cn.woblog.android.downloader.DOWNLOAD_SERVICE" />
      </intent-filter>
    </service>

    添加依赖

    我们提供了多种集成方式,比如:gradle,maven,jar。选择适合你自己的就行了。

    Gradle

    在module目录下面的build.gradle文件中添加如下内容:

    compile 'cn.woblog.android:downloader:1.0.1'

    Maven

    或者你使用的Maven依赖管理工具。那道理其实是一样的,在pom文件中添加:

    <dependency>
      <groupId>cn.woblog.android</groupId>
      <artifactId>downloader</artifactId>
      <version>1.0.0</version>
    </dependency>

    或者你也可以参考该链接使用Snapshots版本

    混淆配置

    如果你的项目使用了混淆规则,那么一定要加上。

    -keep public class * implements cn.woblog.android.downloader.db.DownloadDBController
    -keep class cn.woblog.android.downloader.domain.** { *; }

    创建下载管理器

    现在万事俱备只欠东风了,接下来只需要创建一个下载管理器,该框架所有的操作都是通过该来实现的:

    downloadManager = DownloadService.getDownloadManager(context.getApplicationContext());

    或者你可以使用更详细的来配置该框架:

    Config config = new Config();
    //set database path.
    //    config.setDatabaseName("/sdcard/a/d.db");
    //      config.setDownloadDBController(dbController);
    
    //set download quantity at the same time.
    config.setDownloadThread(3);
    
    //set each download info thread number
    config.setEachDownloadThread(2);
    
    // set connect timeout,unit millisecond
    config.setConnectTimeout(10000);
    
    // set read data timeout,unit millisecond
    config.setReadTimeout(10000);
    downloadManager = DownloadService.getDownloadManager(this.getApplicationContext(), config);

    下载一个文件

    //create download info set download uri and save path.
    final DownloadInfo downloadInfo = new DownloadInfo.Builder().setUrl("http://example.com/a.apk")
        .setPath("/sdcard/a.apk")
        .build();
    
    //set download callback.
    downloadInfo.setDownloadListener(new DownloadListener() {
    
      @Override
      public void onStart() {
        tv_download_info.setText("Prepare downloading");
      }
    
      @Override
      public void onWaited() {
        tv_download_info.setText("Waiting");
        bt_download_button.setText("Pause");
      }
    
      @Override
      public void onPaused() {
        bt_download_button.setText("Continue");
        tv_download_info.setText("Paused");
      }
    
      @Override
      public void onDownloading(long progress, long size) {
        tv_download_info
            .setText(FileUtil.formatFileSize(progress) + "/" + FileUtil
                .formatFileSize(size));
        bt_download_button.setText("Pause");
      }
    
      @Override
      public void onRemoved() {
        bt_download_button.setText("Download");
        tv_download_info.setText("");
        downloadInfo = null;
      }
    
      @Override
      public void onDownloadSuccess() {
        bt_download_button.setText("Delete");
        tv_download_info.setText("Download success");
      }
    
      @Override
      public void onDownloadFailed(DownloadException e) {
        e.printStackTrace();
        tv_download_info.setText("Download fail:" + e.getMessage());
      }
    });
    
    //submit download info to download manager.
    downloadManager.download(downloadInfo);

    下载一个文件时直接创建一个DownloadInfo,然后设置下载链接和下载路径。再添加一个监听。就可以提交到下载框架了。

    通过下载监听器我们可以获取到很多状态。开始下载,等待中,暂停完成,下载中,删除成功,下载成功,下载失败等状态。

    在列表控件使用

    我们这里演示如何在RecyclerView这类列表控件使用。当然如果你用的是ListView那道理是一样的。

    class ViewHolder extends RecyclerView.ViewHolder {
    
      private final ImageView iv_icon;
      private final TextView tv_size;
      private final TextView tv_status;
      private final ProgressBar pb;
      private final TextView tv_name;
      private final Button bt_action;
      private DownloadInfo downloadInfo;
    
      public ViewHolder(View view) {
        super(view);
    
        iv_icon = (ImageView) view.findViewById(R.id.iv_icon);
        tv_size = (TextView) view.findViewById(R.id.tv_size);
        tv_status = (TextView) view.findViewById(R.id.tv_status);
        pb = (ProgressBar) view.findViewById(R.id.pb);
        tv_name = (TextView) view.findViewById(R.id.tv_name);
        bt_action = (Button) view.findViewById(R.id.bt_action);
      }
    
      @SuppressWarnings("unchecked")
      public void bindData(final MyDownloadInfo data, int position, final Context context) {
        Glide.with(context).load(data.getIcon()).into(iv_icon);
        tv_name.setText(data.getName());
    
        // Get download task status
        downloadInfo = downloadManager.getDownloadById(data.getUrl().hashCode());
    
        // Set a download listener
        if (downloadInfo != null) {
          downloadInfo
              .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
                //  Call interval about one second
                @Override
                public void onRefresh() {
                  if (getUserTag() != null && getUserTag().get() != null) {
                    ViewHolder viewHolder = (ViewHolder) getUserTag().get();
                    viewHolder.refresh();
                  }
                }
              });
    
        }
    
        refresh();
    
    //      Download button
        bt_action.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(View v) {
            if (downloadInfo != null) {
    
              switch (downloadInfo.getStatus()) {
                case DownloadInfo.STATUS_NONE:
                case DownloadInfo.STATUS_PAUSED:
                case DownloadInfo.STATUS_ERROR:
    
                  //resume downloadInfo
                  downloadManager.resume(downloadInfo);
                  break;
    
                case DownloadInfo.STATUS_DOWNLOADING:
                case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
                case STATUS_WAIT:
                  //pause downloadInfo
                  downloadManager.pause(downloadInfo);
                  break;
                case DownloadInfo.STATUS_COMPLETED:
                  downloadManager.remove(downloadInfo);
                  break;
              }
            } else {
    //            Create new download task
              File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d");
              if (!d.exists()) {
                d.mkdirs();
              }
              String path = d.getAbsolutePath().concat("/").concat(data.getName());
              downloadInfo = new Builder().setUrl(data.getUrl())
                  .setPath(path)
                  .build();
              downloadInfo
                  .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
    
                    @Override
                    public void onRefresh() {
                      if (getUserTag() != null && getUserTag().get() != null) {
                        ViewHolder viewHolder = (ViewHolder) getUserTag().get();
                        viewHolder.refresh();
                      }
                    }
                  });
              downloadManager.download(downloadInfo);
            }
          }
        });
    
      }
    
      private void refresh() {
        if (downloadInfo == null) {
          tv_size.setText("");
          pb.setProgress(0);
          bt_action.setText("Download");
          tv_status.setText("not downloadInfo");
        } else {
          switch (downloadInfo.getStatus()) {
            case DownloadInfo.STATUS_NONE:
              bt_action.setText("Download");
              tv_status.setText("not downloadInfo");
              break;
            case DownloadInfo.STATUS_PAUSED:
            case DownloadInfo.STATUS_ERROR:
              bt_action.setText("Continue");
              tv_status.setText("paused");
              try {
                pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
              } catch (Exception e) {
                e.printStackTrace();
              }
              tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                  .formatFileSize(downloadInfo.getSize()));
              break;
    
            case DownloadInfo.STATUS_DOWNLOADING:
            case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
              bt_action.setText("Pause");
              try {
                pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
              } catch (Exception e) {
                e.printStackTrace();
              }
              tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                  .formatFileSize(downloadInfo.getSize()));
              tv_status.setText("downloading");
              break;
            case STATUS_COMPLETED:
              bt_action.setText("Delete");
              try {
                pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
              } catch (Exception e) {
                e.printStackTrace();
              }
              tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                  .formatFileSize(downloadInfo.getSize()));
              tv_status.setText("success");
              break;
            case STATUS_REMOVED:
              tv_size.setText("");
              pb.setProgress(0);
              bt_action.setText("Download");
              tv_status.setText("not downloadInfo");
            case STATUS_WAIT:
              tv_size.setText("");
              pb.setProgress(0);
              bt_action.setText("Pause");
              tv_status.setText("Waiting");
              break;
          }
    
        }
      }
    }

    关键代码就是bindData方法中先通过业务的id,我们这里使用的url来获取该业务数据是否有对应的下载任务。如果有,则从新绑定监听器,也就是这段代码

    downloadInfo
      .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
        //  Call interval about one second
        @Override
        public void onRefresh() {
          if (getUserTag() != null && getUserTag().get() != null) {
            ViewHolder viewHolder = (ViewHolder) getUserTag().get();
            viewHolder.refresh();
          }
        }
      });

    其中要注意到的是缓存每个条目我们使用了SoftReference,这样做的目的内容在吃紧的情况下而已及时的是否这些条目。

    接下来又一个重要的点是,设置按钮的点击事件,通常在这样的列表中有一个或多个按钮控制下载状态。

    if (downloadInfo != null) {
    
      switch (downloadInfo.getStatus()) {
        case DownloadInfo.STATUS_NONE:
        case DownloadInfo.STATUS_PAUSED:
        case DownloadInfo.STATUS_ERROR:
    
          //resume downloadInfo
          downloadManager.resume(downloadInfo);
          break;
    
        case DownloadInfo.STATUS_DOWNLOADING:
        case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
        case STATUS_WAIT:
          //pause downloadInfo
          downloadManager.pause(downloadInfo);
          break;
        case DownloadInfo.STATUS_COMPLETED:
          downloadManager.remove(downloadInfo);
          break;
      }
    } else {
    //            Create new download task
      File d = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "d");
      if (!d.exists()) {
        d.mkdirs();
      }
      String path = d.getAbsolutePath().concat("/").concat(data.getName());
      downloadInfo = new Builder().setUrl(data.getUrl())
          .setPath(path)
          .build();
      downloadInfo
          .setDownloadListener(new MyDownloadListener(new SoftReference(ViewHolder.this)) {
    
            @Override
            public void onRefresh() {
              if (getUserTag() != null && getUserTag().get() != null) {
                ViewHolder viewHolder = (ViewHolder) getUserTag().get();
                viewHolder.refresh();
              }
            }
          });
      downloadManager.download(downloadInfo);
    }

    关键点就是如果没有下载任务就创建一个下载任务,如果已有下载任务就根据任务现在的状态执行相应的操作,比如当前是没有下载,点击就是创建一个下载任务。

    接下还有一个重点就是,我们在回调监听中调用了refresh方法,在该方法中根据状态显示进度和相应的操作按钮。这样做的好处上面已经提到了。

    private void refresh() {
      if (downloadInfo == null) {
        tv_size.setText("");
        pb.setProgress(0);
        bt_action.setText("Download");
        tv_status.setText("not downloadInfo");
      } else {
        switch (downloadInfo.getStatus()) {
          case DownloadInfo.STATUS_NONE:
            bt_action.setText("Download");
            tv_status.setText("not downloadInfo");
            break;
          case DownloadInfo.STATUS_PAUSED:
          case DownloadInfo.STATUS_ERROR:
            bt_action.setText("Continue");
            tv_status.setText("paused");
            try {
              pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
            } catch (Exception e) {
              e.printStackTrace();
            }
            tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                .formatFileSize(downloadInfo.getSize()));
            break;
    
          case DownloadInfo.STATUS_DOWNLOADING:
          case DownloadInfo.STATUS_PREPARE_DOWNLOAD:
            bt_action.setText("Pause");
            try {
              pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
            } catch (Exception e) {
              e.printStackTrace();
            }
            tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                .formatFileSize(downloadInfo.getSize()));
            tv_status.setText("downloading");
            break;
          case STATUS_COMPLETED:
            bt_action.setText("Delete");
            try {
              pb.setProgress((int) (downloadInfo.getProgress() * 100.0 / downloadInfo.getSize()));
            } catch (Exception e) {
              e.printStackTrace();
            }
            tv_size.setText(FileUtil.formatFileSize(downloadInfo.getProgress()) + "/" + FileUtil
                .formatFileSize(downloadInfo.getSize()));
            tv_status.setText("success");
            break;
          case STATUS_REMOVED:
            tv_size.setText("");
            pb.setProgress(0);
            bt_action.setText("Download");
            tv_status.setText("not downloadInfo");
          case STATUS_WAIT:
            tv_size.setText("");
            pb.setProgress(0);
            bt_action.setText("Pause");
            tv_status.setText("Waiting");
            break;
        }
    
      }
    }

    到这里改下框架的核心使用方法就介绍完了。

    支持

    如有任何问题可以在加我们的QQ群或者在Github上提Issue,另外请提Issue或者的PR的一定要看下项目的贡献代码的方法以及一要求,因为如果要保证一个开源项目的质量就必须在各方面都规范化。

    展开全文
  • android的一个下载框架Aria

    千次阅读 2017-08-29 14:43:51
    Aria项目源于15年工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标
  • IDEA2016 创建项目的时候添加struts2和hibernate框架,需要下载相应库,但是搭了梯子也是下载失败,如何将所有的库都下载为离线使用?或者国内的镜像下载站点怎么配置
  • Android多线程多任务下载框架
  • FileDownloader(https://github.com/wlfcolin/file-downloader)是本人开源的一个安卓Http文件下载框架,是根据自己的经验总结的一套非常轻量级的安卓通用Http文件下载管理器。我的目标是让文件下载越简单越好,尽...
  • Aria下载框架的使用方法

    千次阅读 2019-08-02 11:28:58
    2.下载回调方法,去更新进度条,和暂停、失败、下载、开始等状态 注意:自定义按钮状态,随着回调的状态改变自己的状态 ... 此处刚关闭页面重新进来时,用框架给的状态也是不对的,当你本应是等待状态时,却还是...
  • android 牛逼的下载框架

    千次阅读 2017-11-24 11:31:25
    最新在开发的过程碰到一个问题,就是apk的下载成功率一直不高,用的下载器是android系统的下载器DownloadManager,保持在50%多成功率,因为我们一些apk包很大,很多都是300,400M的游戏包,下载成功率低从而也会导致...
  • 1)Glide 框架简介 Android Glide 是一个开源的图片加载和缓存处理的第三方框架。和 Android 的 Picasso 库类似,个人感觉比 Android Picasso 好用。Android Glide 使自身内部 已经实现了缓存策略,使得开发者摆脱 ...
  • 首先,依赖: ...很不错的一个下载框架,性能很好; 更多的使用可以去github上去看文档,有中英文的 github地址: https://github.com/lingochamp/FileDownloader        
  • 获取【下载地址】 QQ: 313596790 【免费支持更新】 A 代码生成器(开发利器);全部是源码   增删改查的处理类,service层,mybatis的xml,SQL( mysql 和oracle)脚本, jsp页面 都生成  就不用写搬砖的代码了,...
  • SSH框架总结(框架分析+环境搭建+实例源码下载

    万次阅读 多人点赞 2013-04-25 10:00:28
    首先,SSH不是一个框架,而是多个框架(struts+spring+hibernate)的集成,是目前较流行的一种Web应用程序开源集成框架,用于构建灵活、易于扩展的多层Web应用程序。   集成SSH框架的系统从职责上分为四层:表示层...
  • Spring+SpringMVC+Mybatis框架整合例子(SSM) 下载

    万次下载 热门讨论 2014-07-21 12:06:07
    本资源对应博文:http://blog.csdn.net/zhshulin/article/details/37956105,可以通过博文进行学习,不建议下载完整源码,博文有详细教程,以及代码。
  • 微软框架下载

    千次阅读 2017-10-01 10:07:19
    微软 提供的最新 .net Framework 4.7 之前只是可以在创造者更新使用,现在支持很多的版本的系统。 本文提供 .net core 2.0 的离线下载安装包。.net Framework 4.7 以及其他版本的下载地址
  • Spring框架下载

    千次阅读 2014-09-07 10:43:27
    spring官网现在下载方式提供的只有maven和gradle,所以下载spring,需要先了解maven或者gradle,这样就增加了一些小白的学习成本,本来想集成spring框架,却还得学习maven或gradle,在这不是说这些工具不好,虽然...
  • composer下载laravel框架

    千次阅读 2020-01-08 11:31:05
    下载并且运行 Composer-Setup.exe,它将安装最新版本的 Composer ,并设置好系统的环境变量,因此你可以在任何目录下直接使用 composer 命令。 一直下一步,结束安装! 设置环境变量 composer安装完成! 在cmd...
  • FileDownloader(https://github.com/wlfcolin/file-downloader)是本人开源的一个安卓Http文件下载框架,是根据自己的经验总结的一套非常轻量级的安卓通用Http文件下载管理器。我的目标是让文件下载越简单越好,尽...
  • 如何下载Spring框架

    万次阅读 多人点赞 2018-11-09 19:53:08
    首先进入Spring官网: https://spring.io/ 点击PROJECTS   找到SPRING FRAMEWORK 并点击 ...然后点击一个连接进行下载即可     ( 中智软件科技学校 )        
  • Scrapy框架之利用ImagesPipeline下载图片

    千次阅读 2017-05-29 15:21:37
    Scrapy框架之利用ImagesPipeline下载图片

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 70,483
精华内容 28,193
关键字:

下载框架