精华内容
下载资源
问答
  • 安卓下载任务管理 > 前言:上年开发了一个壁纸,...单例,创建下载任务,提供获取正在下载任务,所有下载信息,设置监听器等接口 DownloadEngine 负责创建线程池,根据任务创建下载工作,调度工作及通知 Download
  • Android实现网络下载二(多任务下载–支持断点续传) 上文中说了单任务的断点续传,这篇文章就说说多任务下载,不啰嗦了,直接进入正题。 附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。 点这里下载...

    Android实现网络下载二(多任务下载–支持断点续传)

    上文中说了单任务的断点续传,这篇文章就说说多任务下载,不啰嗦了,直接进入正题。

    附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

    点这里下载源码,快,戳我戳我…

    q:486789970
    email:mr.cai_cai@foxmail.com

    下图是一个多任务下载的动态图:

    效果图如下(单任务下载在上篇文章)https://blog.csdn.net/qq_35840038/article/details/90239354

    在上篇博客基础上,新增了一个ListView用来显示多条下载内容,这个很简单,这里就不多说啦
    在这里插入图片描述
    .上面的效果图就是多任务下载(使用线程池管理),支持断点续传、实时进度更新、下载暂停、下载继续,下载完成自动安装等功能;同时包括网络下载请求和本地文件的存储。

    实现原理

    • 首先通过Service里面的代码获得下载文件的长度,然后设置本地文件的长度
    • 根据文件长度和线程数计算每条线程下载的数据长度和下载位置(这里用了三条线程,当然也可改成让用户输入线程数)
    • 文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M

    例如10M大小,使用3个线程来下载

    具体的流程在上篇文章中说过了,多任务的只是修改了一点东西而已。直接看修改的代码:

    Service

    package com.cc.downloaddemo.services;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.support.annotation.Nullable;
    import com.cc.downloaddemo.info.FileInfo;
    import java.io.File;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * 下载service
     */
    public class DownloadService extends Service {
    
        //通过URL地址下载apk文件并保存在本地存储路径
        public static final String DOWNLOAD_PATH =
                Environment.getExternalStorageDirectory().getAbsolutePath() +
                        "/downloads/";
    
        //开始下载
        public static final String ACTION_START = "ACTION_START";
    
        //暂停下载
        public static final String ACTION_STOP = "ACTION_STOP";
    
        //结束下载
        public static final String ACTION_FINISH = "ACTION_FINISH";
    
        //更新下载进度
        public static final String ACTION_UPDATE = "ACTION_UPDATE";
    
        //定义handler的flag
        public static final int MSG_INIT = 0;
    
        //异步下载任务集合(设置为map集合,查找方便一些)
        private Map<Integer, DownloadTask> taskMap = new LinkedHashMap<>();
    
        /**
         * 执行下载、暂停、继续下载
         * @param intent
         * @param flags
         * @param startId
         * @return
         */
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            //获取从Activity传过去的参数,判断intent的值。
            if(ACTION_START.equals(intent.getAction())){
                FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
                //启动初始化子线程,联网下载文件内容。
                InitThread initThread = new InitThread(fileInfo);
                //使用线程池来管理
                DownloadTask.executorService.execute(initThread);
            } else if (ACTION_STOP.equals(intent.getAction())) {
                //暂停下载时,更改下载任务类的flag便会自动暂停。
                FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
                //从集合中取出下载任务
                DownloadTask task = taskMap.get(fileInfo.getId());
                //非空处理
                if(task != null){
                    //停止下载任务
                    task.ispause = true;
                }
            }
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        /**
         * 网络内容下载完成后
         * 通过handler启动异步任务类开始正式下载文件。
         */
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                //是否为初始化消息
                if(msg.what == MSG_INIT){
                    //获取发回来的信息
                    FileInfo fileInfo = (FileInfo) msg.obj;
                    //启动一个下载任务,将文件内容传递过去。
                    //一个任务让三个线程去下载
                    DownloadTask downloadTask = new DownloadTask(DownloadService.this, fileInfo, 3);
                    //启动下载方法
                    downloadTask.download();
                    //把下载任务添加到集合中
                    taskMap.put(fileInfo.getId(), downloadTask);
                }
            }
        };
    
        /**
         * 定义子线程用来下载
         */
        class InitThread extends Thread{
    
            //定义一个文件用来接收下载信息
            private FileInfo mFileInfo = null;
    
            //启动线程时传入的对象
            public InitThread(FileInfo mFileInfo) {
                this.mFileInfo = mFileInfo;
            }
    
            @Override
            public void run() {
                //定义conn
                HttpURLConnection conn = null;
                //定义文件内容访问类
                RandomAccessFile raf = null;
                try {
                    //连接网络文件
                    URL url = new URL(mFileInfo.getUrl());
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setReadTimeout(3000);
                    conn.setRequestMethod("GET");
    
                    //自定义一个长度,注意尽量用long(处理下载消息进度百分比时好计算)
                    long length = -1;
                    //下载完成
                    if(conn.getResponseCode() == HttpURLConnection.HTTP_OK ){
                        //获取文件总长度赋值给length
                        length = conn.getContentLength();
                    }
                    //长度不能小于0
                    if(length <= 0){
                        return;
                    }
                    //判断该路径存在与否
                    File dir = new File(DOWNLOAD_PATH);
                    //文件不存在时创建
                    if(!dir.exists()){
                        dir.mkdir();
                    }
                    //本地创建一个文件
                    File file = new File(dir, mFileInfo.getFilename());
                    //输出流
                    raf = new RandomAccessFile(file, "rwd");
                    //设置本地的文件长度(等于下载文件的总长度。杜绝浪费资源)
                    raf.setLength(length);
                    //赋值给定义的对象长度
                    mFileInfo.setLength(length);
                    //启动flag,将文件发送给handler处理
                    handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    try {
                        //关闭资源
                        raf.close();
                        conn.disconnect();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
    
    注:使用了线程池统一管理
    

    DownloadTask:

    package com.cc.downloaddemo.services;
    
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.util.Log;
    import com.cc.downloaddemo.db.dao.ThreadDao;
    import com.cc.downloaddemo.db.impl.ThreadDaoImpl;
    import com.cc.downloaddemo.info.FileInfo;
    import com.cc.downloaddemo.info.ThreadInfo;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 任务下载类
     * @author
     */
    public class DownloadTask {
    
        //上下文
        private Context context;
    
        //定义数据库线程对象文件
        private FileInfo fileInfo;
    
        //实例化接口
        public static ThreadDao threadDao = null;
    
        //定义finished(当前已下载的长度)
        private int finished = 0;
    
        //默认直接下载
        public boolean ispause = false;
    
        //默认线程数量
        private int threadCount = 1;
    
        //定义线程集合
        private List<DownloadThread> threadList;
    
        //定义线程池
        public static ExecutorService executorService = Executors.newCachedThreadPool();
    
        /**
         * 构造方法
         * @param context
         * @param fileInfo
         */
        public DownloadTask(Context context, FileInfo fileInfo, int threadCount) {
            this.context = context;
            //拿到handler传过来的文件
            this.fileInfo = fileInfo;
            //拿到线程数量
            this.threadCount = threadCount;
    
            //实例化接口实现类
            threadDao = new ThreadDaoImpl(context);
        }
    
        /**
         * 下载方法
         */
        public void download(){
            //下载任务一开始,先查询数据库,看是否有文件在等待下载。
            //读取数据库的所有线程信息
            List<ThreadInfo> threads = threadDao.getThreads(fileInfo.getUrl());
            //当list的size为0时,说明没有等待下载的线程,为1时则有。
            if(threads.size() == 0){
                //获得每一个文件
                int length = (int) (fileInfo.getLength() / threadCount);
                for (int i = 0; i < threadCount; i++){
                    //创建线程信息
                    ThreadInfo threadInfo = new ThreadInfo(i, fileInfo.getUrl()
                            , length * i, (i + 1) * length - 1, 0);
                    //当i是最后一个线程时,设置一个索引
                    if(i == threadCount - 1){
                        threadInfo.setEnd((int) fileInfo.getLength());
                    }
                    //添加到线程信息集合中
                    threads.add(threadInfo);
                    threadDao.insertThread(threadInfo);
                }
            }
            threadList = new ArrayList<>();
            //启动多个线程进行下载
            for (ThreadInfo info : threads){
                DownloadThread downloadThread = new DownloadThread(info);
    //            downloadThread.start();
                DownloadTask.executorService.execute(downloadThread);
                //添加线程到集合中
                threadList.add(downloadThread);
            }
        }
    
        /**
         * 判断是否所有线程都执行完毕
         * 保证同一时间段只有一个线程访问此方法
         */
        private synchronized void checkAddThreadFinished(){
            //假设全部完成啦
            boolean allFinished = true;
    
            //遍历集合
            for (DownloadThread thread : threadList){
                if(!thread.isFinished){
                    allFinished = false;
                    break;
                }
            }
            if(allFinished){
                //下载完成,删除数据库所保存的线程信息
                threadDao.deleteThread(fileInfo.getUrl());
                //发送广播通知,消灾任务结束
                Intent intent = new Intent(DownloadService.ACTION_FINISH);
                intent.putExtra("fileInfo", fileInfo);
                context.sendBroadcast(intent);
            }
        }
    
        /**
         * 下载线程
         */
        class DownloadThread extends Thread{
    
            //继续定义一个临时线程对象
            private ThreadInfo threadInfo;
    
            //标记线程是否结束
            public boolean isFinished = false;
    
            //构造
            public DownloadThread(ThreadInfo threadInfo) {
                this.threadInfo = threadInfo;
            }
    
            @Override
            public void run() {
                //定义conn、输入流和文件内容访问类
                HttpURLConnection conn = null;
                InputStream inputStream = null;
                RandomAccessFile raf = null;
    
                try{
                    //开始联网下载
                    URL url = new URL(threadInfo.getUrl());
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setReadTimeout(3000);
                    conn.setRequestMethod("GET");
    
                    //设置下载位置(通过当前开始值和现在值判断下载位置)
                    int start = threadInfo.getStart() + threadInfo.getFinished();
                    conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());
                    //设置文件写入位置
                    File file = new File(DownloadService.DOWNLOAD_PATH, fileInfo.getFilename());
                    raf = new RandomAccessFile(file, "rwd");
                    raf.seek(start);
                    //定义一个广播,用来更新下载进度
                    Intent intent = new Intent(DownloadService.ACTION_UPDATE);
    
                    //
                    finished += threadInfo.getFinished();
                    //开始下载
                    if(conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
                        //读取数据
                        inputStream = conn.getInputStream();
                        byte[] buffer = new byte[1024 * 4];
                        int len = -1;
                        long time = System.currentTimeMillis();
                        while ((len = inputStream.read(buffer)) != -1){
                            //写入文件
                            raf.write(buffer, 0, len);
                            //累加整个文件的下载完成进度
                            finished += len;
                            //累加每个线程完成的进度
                            threadInfo.setFinished(threadInfo.getFinished() + len);
                            //间隔500毫秒更新一下进度
                            if(System.currentTimeMillis() - time > 1500){
                                time = System.currentTimeMillis();
                                intent.putExtra("finished", (int)(finished / (float)fileInfo.getLength() * 100));
                                intent.putExtra("id", fileInfo.getId());
                                context.sendBroadcast(intent);
                            }
                            //在下载暂停时,保存下载进度
                            if(ispause){
                                threadDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished());
                                return;
                            }
                        }
                        intent.putExtra("finished", 100);
                        context.sendBroadcast(intent);
                        //标识线程执行完毕
                        isFinished = true;
                        //执行完之后检查
                        checkAddThreadFinished();
                        openFile(file);
                    }
                }catch (Exception e){
                    e.printStackTrace();;
                }finally {
                    conn.disconnect();
                    try {
                        inputStream.close();
                        raf.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 自动安装
         * @param file
         */
        private void openFile(File file) {
            // TODO Auto-generated method stub
            Log.e("OpenFile", file.getName());
            Intent intent = new Intent();
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setAction(android.content.Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
            context.startActivity(intent);
        }
    
    }
    
    

    附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

    点这里下载源码,快,戳我戳我…

    q:486789970
    email:mr.cai_cai@foxmail.com

    如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

    												---财财亲笔
    
    展开全文
  • 多任务下载在android app中很多应用场景,比如应用...包括:网络下载请求,下载任务执行、下载任务调度、UI进度更新、任务状态变化、文件的存储。二、框架流程三、框架代码:下面着重分析下DownloadTask和TaskDisp...

    多任务下载在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);

    七、效果图



    
    

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




    展开全文
  • 做移动软件开发,必然要涉及软件版本升级。版本检测什么的我就不多说了,网上一大堆,这里主要是在获取新版本...= 0) { //根据任务ID判断是否存在相同的下载任务,如果有则清除 clearCurrentTask(mContext, downloa...

            做移动软件开发,必然要涉及软件版本升级。版本检测什么的我就不多说了,网上一大堆,这里主要是在获取新版本APK地址后的下载操作。

    第一步:判断任务是否已经存在如果存在,先清除原任务

    if (downloadId != 0) {  //根据任务ID判断是否存在相同的下载任务,如果有则清除
                clearCurrentTask(mContext, downloadId);
            }
            downloadId = downLoadApk(mContext, url, describeStr);

    第二步:开启下载任务

    public static long downLoadApk(Context context, String url, String describeStr) {
            // 得到系统的下载管理
            DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
            Uri uri = Uri.parse(url);
            // 以下两行代码可以让下载的apk文件被直接安装而不用使用Fileprovider,系统7.0或者以上才启动。
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
                StrictMode.setVmPolicy(localBuilder.build());
            }
            DownloadManager.Request requestApk = new DownloadManager.Request(uri);
            // 设置在什么网络下下载
            requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
            // 下载中和下载完后都显示通知栏
            requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            if (saveFile.exists()) {    //判断文件是否存在,存在的话先删除
                saveFile.delete();
            }
            requestApk.setDestinationUri(Uri.fromFile(saveFile));
            // 表示允许MediaScanner扫描到这个文件,默认不允许。
            requestApk.allowScanningByMediaScanner();
            // 设置下载中通知栏的提示消息
            requestApk.setTitle("XXXX更新下载");
            // 设置设置下载中通知栏提示的介绍
            requestApk.setDescription(describeStr);
    
            // 7.0以上的系统适配
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                requestApk.setRequiresDeviceIdle(false);
                requestApk.setRequiresCharging(false);
            }
            // 启动下载,该方法返回系统为当前下载请求分配的一个唯一的ID
            long downLoadId = manager.enqueue(requestApk);
            return downLoadId;
        }

    开启下载任务后返回了任务ID,这是任务的唯一标识。

    第三步:下载完成后调用广播,通知系统去执行安装操作点击任务栏时判断任务是否下载完成

    if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
                DownloadApkUtils.installApk(context);
            } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
                // 如果还未完成下载,用户点击Notification ,跳转到下载中心
                Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
                viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(viewDownloadIntent);
            }

    第四部:检测和安装APK

    public static void installApk(Context context) {
            downloadId = 0;
    
            Intent intent = new Intent(Intent.ACTION_VIEW);
            try {
                String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
                ProcessBuilder builder = new ProcessBuilder(command);
                builder.start();
            } catch (Exception ignored) {
                ignored.printStackTrace();
            }
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            } else {
                intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
    
            context.startActivity(intent);
        }

    下面给出关键类的代码:

    (1)DownloadApkUtils:

    public class DownloadApkUtils {
        private static File saveFile;
        private static long downloadId = 0;
    
        public static void startDownload(Context mContext, String url, String describeStr) {
            initFile();
            if (downloadId != 0) {  //根据任务ID判断是否存在相同的下载任务,如果有则清除
                clearCurrentTask(mContext, downloadId);
            }
            downloadId = downLoadApk(mContext, url, describeStr);
        }
    
        private static void initFile() {
            if (saveFile == null)
                saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "XXXX.apk");
        }
    
        public static long downLoadApk(Context context, String url, String describeStr) {
            // 得到系统的下载管理
            DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
            Uri uri = Uri.parse(url);
            // 以下两行代码可以让下载的apk文件被直接安装而不用使用Fileprovider,系统7.0或者以上才启动。
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
                StrictMode.setVmPolicy(localBuilder.build());
            }
            DownloadManager.Request requestApk = new DownloadManager.Request(uri);
            // 设置在什么网络下下载
            requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
            // 下载中和下载完后都显示通知栏
            requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            if (saveFile.exists()) {    //判断文件是否存在,存在的话先删除
                saveFile.delete();
            }
            requestApk.setDestinationUri(Uri.fromFile(saveFile));
            // 表示允许MediaScanner扫描到这个文件,默认不允许。
            requestApk.allowScanningByMediaScanner();
            // 设置下载中通知栏的提示消息
            requestApk.setTitle("XXXX更新下载");
            // 设置设置下载中通知栏提示的介绍
            requestApk.setDescription(describeStr);
    
            // 7.0以上的系统适配
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                requestApk.setRequiresDeviceIdle(false);
                requestApk.setRequiresCharging(false);
            }
            // 启动下载,该方法返回系统为当前下载请求分配的一个唯一的ID
            long downLoadId = manager.enqueue(requestApk);
            return downLoadId;
        }
    
        /**
         * 下载前先移除前一个任务,防止重复下载
         *
         * @param downloadId
         */
        public static void clearCurrentTask(Context mContext, long downloadId) {
            DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
            try {
                dm.remove(downloadId);
            } catch (IllegalArgumentException ex) {
                ex.printStackTrace();
            }
        }
    
        public static void installApk(Context context) {
            downloadId = 0;
    
            Intent intent = new Intent(Intent.ACTION_VIEW);
            try {
                String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
                ProcessBuilder builder = new ProcessBuilder(command);
                builder.start();
            } catch (Exception ignored) {
                ignored.printStackTrace();
            }
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            } else {
                intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
    
            context.startActivity(intent);
        }
    
    }

    要想安装要有权限,当然还要有读写权限以及网络请求权限,这里就不列出来了

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

    (2)下载的广播接收者

    public class DownloadReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
                DownloadApkUtils.installApk(context);
            } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
                // 如果还未完成下载,用户点击Notification ,跳转到下载中心
                Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
                viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(viewDownloadIntent);
            }
        }
    }

    既然用到了广播接收这,记得在配置文件中注册;

    <receiver android:name=".receiver.DownloadReceiver">
          <intent-filter>
               <action android:name="android.intent.action.DOWNLOAD_SERVICE"/>
          </intent-filter>
    </receiver>

    执行下载操作的Activity中注册广播

    private boolean isRegisterReceiver = false;
        /**
         * 注册下载成功的广播监听
         */
        private void setReceiver() {
            if (!isRegisterReceiver) {
                DownloadReceiver receiver = new DownloadReceiver();
                IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
                this.registerReceiver(receiver, intentFilter);
                isRegisterReceiver = true;
            }
        }

    都知道android的适配是很烦人的,但是不得不适配。下载过程中涉及了文件的读写,那就必须要适配7.0 使用Provider。

            <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="com.example.retrofitdemo.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_paths" />
            </provider>

    资源文件夹下创建xml文件夹,创建file_paths。内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <external-path name="external_files" path="."/>
    </paths>

    最后就是调用了,由于前面准备工作做得很好,这一步就非常简单了,只要一行代码

    DownloadApkUtils.startDownload(MainActivity.this,"下载地址","下载任务描述");

    到此版本更新时APK的下载及自动安装操作完成。如果对你有帮助,点个赞再走吧。(手动滑稽)

    展开全文
  • 以下代码实现功能有:多线程下载、多任务并行下载以及下载进度和下载速度的显示等功能。 实现思路:根据线程数分割待下载文件;利用HttpURLConnection实现各部分文件的...各线程下载任务信息保存在数据库,以便暂

    以下代码是基于百度云网盘:http://pan.baidu.com/s/1dD1Xo8T 中的demo进行优化及功能添加。
    以下代码实现功能有:多线程下载、多任务并行下载以及下载进度和下载速度的显示等功能。
    实现思路:根据线程数分割待下载文件;利用HttpURLConnection实现各部分文件的下载;利用RandomAccessFile实现下载内容的保存;各线程下载任务信息保存在数据库,以便暂停和恢复下载。
    demo已上传到github:https://github.com/shichaohui/FileDownloadDemo.git 欢迎下载。
    效果图:
    这里写图片描述

    主要代码

    DownLoadHelper.java:

    package com.example.test;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * 利用数据库来记录下载信息
     * 
     * @author shichaohui@meiriq.com
     */
    public class DownLoadHelper extends SQLiteOpenHelper {
    
        private static final String DB_NAME = "download.db";
        private static final String TB_NAME = "download_info";
        private static final int DOWNLOAD_VERSION = 1;
    
        public DownLoadHelper(Context context) {
            super(context, DB_NAME, null, DOWNLOAD_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table "
                    + TB_NAME
                    + "(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
                    + "start_pos integer, end_pos integer, compelete_size integer,url char)");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    
    }
    

    这个类没什么好说的,就是创建数据库和数据表。

    DownlaodSqlTool.java

    package com.example.test;
    
    import java.util.ArrayList;
    import java.util.List;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    
    /**
     * 数据库操作工具类
     * 
     * @author shichaohui@meiriq.com
     */
    public class DownlaodSqlTool {
    
        private static DownlaodSqlTool instance = null;
        private DownLoadHelper dbHelper = null;
    
        private DownlaodSqlTool(Context context) {
            dbHelper = new DownLoadHelper(context);
        }
    
        private static synchronized void syncInit(Context context) {
            if (instance == null) {
                instance = new DownlaodSqlTool(context);
            }
        }
    
        public static DownlaodSqlTool getInstance(Context context) {
            if (instance == null) {
                syncInit(context);
            }
            return instance;
        }
    
        /** 将下载的进度等信息保存到数据库 */
        public void insertInfos(List<DownloadInfo> infos) {
            SQLiteDatabase database = dbHelper.getWritableDatabase();
            for (DownloadInfo info : infos) {
                String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
                Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
                        info.getEndPos(), info.getCompeleteSize(), info.getUrl() };
                database.execSQL(sql, bindArgs);
            }
        }
    
        /** 获取下载的进度等信息 */
        public List<DownloadInfo> getInfos(String urlstr) {
            List<DownloadInfo> list = new ArrayList<DownloadInfo>();
            SQLiteDatabase database = dbHelper.getWritableDatabase();
            String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
            Cursor cursor = database.rawQuery(sql, new String[] { urlstr });
            while (cursor.moveToNext()) {
                DownloadInfo info = new DownloadInfo(cursor.getInt(0),
                        cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
                        cursor.getString(4));
                list.add(info);
            }
            cursor.close();
            return list;
        }
    
        /** 更新数据库中的下载信息 */
        public void updataInfos(int threadId, int compeleteSize, String urlstr) {
            SQLiteDatabase database = dbHelper.getWritableDatabase();
            String sql = "update download_info set compelete_size=? where thread_id=? and url=?";
            Object[] bindArgs = { compeleteSize, threadId, urlstr };
            database.execSQL(sql, bindArgs);
        }
    
        /** 关闭数据库 */
        public void closeDb() {
            dbHelper.close();
        }
    
        /** 删除数据库中的数据 */
        public void delete(String url) {
            SQLiteDatabase database = dbHelper.getWritableDatabase();
            database.delete("download_info", "url=?", new String[] { url });
        }
    }

    单例模式的数据库操作类,主要实现数据的增删改查等操作。

    DownloadInfo.java

    package com.example.test;
    
    /**
     * 保存每个下载线程下载信息类
     * 
     * @author shichaohui@meiriq.com
     */
    public class DownloadInfo {
    
        private int threadId; // 下载线程的id
        private int startPos; // 开始点
        private int endPos; // 结束点
        private int compeleteSize; // 完成度
        private String url; // 下载文件的URL地址
    
        /**
         * 
         * @param threadId
         *            下载线程的id
         * @param startPos
         *            开始点
         * @param endPos
         *            结束点
         * @param compeleteSize
         *            // 已下载的大小
         * @param url
         *            下载地址
         */
        public DownloadInfo(int threadId, int startPos, int endPos,
                int compeleteSize, String url) {
            this.threadId = threadId;
            this.startPos = startPos;
            this.endPos = endPos;
            this.compeleteSize = compeleteSize;
            this.url = url;
        }
    
        public DownloadInfo() {
        }
    
        /** 获取下载地址 */
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        /** 获取下载线程的Id */
        public int getThreadId() {
            return threadId;
        }
    
        public void setThreadId(int threadId) {
            this.threadId = threadId;
        }
    
        /** 获取下载的开始位置 */
        public int getStartPos() {
            return startPos;
        }
    
        public void setStartPos(int startPos) {
            this.startPos = startPos;
        }
    
        /** 获取下载的结束位置 */
        public int getEndPos() {
            return endPos;
        }
    
        public void setEndPos(int endPos) {
            this.endPos = endPos;
        }
    
        /** 获取已下载的大小 */
        public int getCompeleteSize() {
            return compeleteSize;
        }
    
        public void setCompeleteSize(int compeleteSize) {
            this.compeleteSize = compeleteSize;
        }
    
        @Override
        public String toString() {
            return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos
                    + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize
                    + "]";
        }
    }
    

    下载实体类,针对于单个下载线程,保存下载线程对应的文件相关信息,比如当前下载线程负责下载的部分是从文件的哪个点开始的(startPos)、哪个点结束的(endPos)以及当前已经下载了多少(compeleteSize)等信息。

    DownloadingInfo.java

    package com.example.test;
    
    /**
     * 某一任务正在下载时的信息
     * 
     * @author shichaohui@meiriq.com
     * 
     */
    public class DownloadingInfo {
    
        private String kbps = "0"; // 每秒下载速度
        private int secondSize = 0; // 一秒钟累计下载量
        private int fileSize = 0; // 文件大小
    
        public String getKbps() {
            return kbps;
        }
    
        public void setKbps(String kbps) {
            this.kbps = kbps;
        }
    
        public int getSecondSize() {
            return secondSize;
        }
    
        public void setSecondSize(int secondSize) {
            this.secondSize = secondSize;
        }
    
        public int getFileSize() {
            return fileSize;
        }
    
        public void setFileSize(int fileSize) {
            this.fileSize = fileSize;
        }
    
        @Override
        public String toString() {
            return "DownloadingInfo [kbps=" + kbps + ", secondSize=" + secondSize
                    + ", fileSize=" + fileSize + "]";
        }
    
    }
    

    这也是一个下载相关的实体类,针对于一个下载任务(包括多个下载线程)。保存下载任务的下载进度、速度等用于客户端显示的数据。

    DownloadHttpTool.java

    package com.example.test;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.content.Context;
    import android.os.AsyncTask;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    
    /**
     * 利用Http协议进行多线程下载具体实现类
     * 
     * @author shichaohui@meiriq.com
     */
    public class DownloadHttpTool {
    
        private final int THREAD_COUNT = 2; // 线程数量
        private String urlstr = ""; // URL地址
        private Context mContext = null;
        private List<DownloadInfo> downloadInfos = null; // 保存下载信息的类
        /** 下载文件保存路径 */
        public static String filePath = ""; // 目录
        private String fileName = ""; // 文件名
        private String fileNameTmp = ""; // 临时文件名
        /** 临时文件名后缀 */
        public static final String FILE_TMP_SUFFIX = ".tmp";
        private int fileSize = 0; // 文件大小
        private DownlaodSqlTool sqlTool = null; // 文件信息保存的数据库操作类
        private DownloadComplated downloadComplated = null;
        private int totalCompelete = 0;// 所有线程已下载的总数
        private List<DownloadThread> threads = null; // 下载线程
        private Handler handler = null;
    
        // 利用枚举表示下载的几种状态
        private enum Download_State {
            Downloading, Pause, Ready, Compeleted, Exception;
        }
    
        private Download_State state = Download_State.Ready; // 当前下载状态
    
        /**
         * @param context
         *            上下文对象
         * @param downloadComplated
         */
        public DownloadHttpTool(Context context, Handler handler,
                DownloadComplated downloadComplated) {
            super();
            this.mContext = context;
            this.handler = handler;
            this.downloadComplated = downloadComplated;
            sqlTool = DownlaodSqlTool.getInstance(mContext);
            if ("".equals(filePath)) {
                // TODO 根据有无sdcard设置路径
                filePath = Environment.getExternalStorageDirectory()
                        .getAbsolutePath() + "/meiriq-download";
            }
            threads = new ArrayList<DownloadThread>();
        }
    
        /**
         * 开始下载
         * 
         * @param url
         *            下载地址
         */
        public void start(String urlstr) {
            this.urlstr = urlstr;
            String[] ss = urlstr.split("/");
            fileName = ss[ss.length - 1];
            fileNameTmp = fileName + FILE_TMP_SUFFIX;
    
            new AsyncTask<Void, Void, Void>() {
    
                @Override
                protected Void doInBackground(Void... arg0) {
                    // 下载之前首先异步线程调用ready方法做下载的准备工作
                    ready();
                    Message msg = new Message();
                    msg.what = 1;
                    msg.arg1 = fileSize;
                    msg.obj = DownloadHttpTool.this.urlstr;
                    handler.sendMessage(msg);
                    return null;
                }
    
                @Override
                protected void onPostExecute(Void result) {
                    super.onPostExecute(result);
                    // 开始下载
                    startDownload();
                }
            }.execute();
        }
    
        /** 在开始下载之前需要调用ready方法进行配置 */
        private void ready() {
            if (new File(filePath + "/" + fileName).exists()) {
                downloadComplated.onComplated(urlstr);
                return;
            }
            totalCompelete = 0;
            downloadInfos = sqlTool.getInfos(urlstr);
            if (downloadInfos.size() == 0) { // 数据库中没有相关信息
                initFirst();
            } else {
                File file = new File(filePath + "/" + fileNameTmp);
                if (!file.exists()) {
                    sqlTool.delete(urlstr);
                    initFirst();
                } else {
                    fileSize = downloadInfos.get(downloadInfos.size() - 1)
                            .getEndPos();
                    for (DownloadInfo info : downloadInfos) {
                        totalCompelete += info.getCompeleteSize();
                    }
                }
            }
        }
    
        /** 开始下载 */
        private void startDownload() {
            if (downloadInfos != null) {
                if (state == Download_State.Downloading) {
                    return;
                }
                state = Download_State.Downloading;
                for (DownloadInfo info : downloadInfos) { // 开启线程下载
                    DownloadThread thread = new DownloadThread(info.getThreadId(),
                            info.getStartPos(), info.getEndPos(),
                            info.getCompeleteSize(), info.getUrl());
                    thread.start();
                    threads.add(thread);
                }
            }
        }
    
        /** 暂停当前下载任务 */
        public void pause() {
            state = Download_State.Pause;
        }
    
        /** 删除当前下载任务 */
        public void delete() {
            compeleted();
            File file = new File(filePath + "/" + fileNameTmp);
            file.delete();
        }
    
        /** 完成下载 */
        private void compeleted() {
            state = Download_State.Compeleted;
            sqlTool.delete(urlstr);
            downloadComplated.onComplated(urlstr);
        }
    
        /** 获取目标文件大小 */
        public int getFileSize() {
            return fileSize;
        }
    
        /** 获取当前下载的大小 */
        public int getTotalCompeleteSize() {
            return totalCompelete;
        }
    
        /** 第一次下载时进行的初始化 */
        private void initFirst() {
            URL url = null;
            RandomAccessFile accessFile = null;
            HttpURLConnection connection = null;
            try {
                url = new URL(urlstr);
                connection = (HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(5000);
                connection.setRequestMethod("GET");
                fileSize = connection.getContentLength();
                if (fileSize < 0) {
                    return;
                }
    
                File fileParent = new File(filePath);
                if (!fileParent.exists()) {
                    fileParent.mkdir();
                }
                File file = new File(fileParent, fileNameTmp);
                if (!file.exists()) {
                    file.createNewFile();
                }
                // 随机访问文件
                accessFile = new RandomAccessFile(file, "rwd");
                accessFile.setLength(fileSize);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (accessFile != null) {
                    try {
                        accessFile.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    connection.disconnect();
                }
            }
            // 计算每个线程需要下载的大小
            int range = fileSize / THREAD_COUNT;
            // 保存每个线程的下载信息
            downloadInfos = new ArrayList<DownloadInfo>();
            for (int i = 0; i < THREAD_COUNT - 1; i++) {
                DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range
                        - 1, 0, urlstr);
                downloadInfos.add(info);
            }
            // 最后一个线程和前面的处理有点不一样
            DownloadInfo info = new DownloadInfo(THREAD_COUNT - 1,
                    (THREAD_COUNT - 1) * range, fileSize - 1, 0, urlstr);
            downloadInfos.add(info);
            // 插入到数据库
            sqlTool.insertInfos(downloadInfos);
        }
    
        interface DownloadComplated {
    
            /**
             * 下载完成回调
             * 
             * @param urlString
             */
            void onComplated(String urlString);
    
        }
    
        /** 自定义下载线程 */
        private class DownloadThread extends Thread {
    
            private int threadId = 0; // 线程Id
            private int startPos = 0; // 在文件中的开始的位置
            private int endPos = 0; // 在文件中的结束的位置
            private int compeleteSize = 0; // 已完成下载的大小
            private String urlstr = ""; // 下载地址
    
            /**
             * 
             * @param threadId
             *            线程Id
             * @param startPos
             *            在文件中的开始的位置
             * @param endPos
             *            在文件中的结束的位置
             * @param compeleteSize
             *            已完成下载的大小
             * @param urlstr
             *            下载地址
             */
            public DownloadThread(int threadId, int startPos, int endPos,
                    int compeleteSize, String urlstr) {
                this.threadId = threadId;
                this.startPos = startPos;
                this.endPos = endPos;
                this.urlstr = urlstr;
                this.compeleteSize = compeleteSize;
            }
    
            @Override
            public void run() {
                HttpURLConnection connection = null;
                RandomAccessFile randomAccessFile = null;
                InputStream is = null;
                try {
                    randomAccessFile = new RandomAccessFile(filePath + "/"
                            + fileNameTmp, "rwd");
                    randomAccessFile.seek(startPos + compeleteSize);
                    URL url = new URL(urlstr);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setConnectTimeout(5000);
                    connection.setRequestMethod("GET");
                    // 设置请求的数据的范围
                    connection.setRequestProperty("Range", "bytes="
                            + (startPos + compeleteSize) + "-" + endPos);
                    is = connection.getInputStream();
                    byte[] buffer = new byte[6 * 1024]; // 6K的缓存
                    int length = -1;
                    while ((length = is.read(buffer)) != -1) {
                        randomAccessFile.write(buffer, 0, length); // 写缓存数据到文件
                        compeleteSize += length;
                        synchronized (this) { // 加锁保证已下载的正确性
                            totalCompelete += length;
                            Message msg = new Message();
                            msg.what = 0;
                            msg.arg1 = length;
                            msg.arg2 = totalCompelete;
                            msg.obj = urlstr;
                            handler.sendMessage(msg);
                        }
                        // 非正在下载状态时跳出循环
                        if (state != Download_State.Downloading) {
                            break;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("异常退出____" + urlstr);
                    state = Download_State.Exception;
                } finally {
                    // 不管发生了什么事,都要保存下载信息到数据库
                    sqlTool.updataInfos(threadId, compeleteSize, urlstr);
                    if (threads.size() == 1) { // 当前线程是此url对应下载任务唯一一个正在执行的线程
                        try {
                            if (is != null) {
                                is.close();
                            }
                            if (randomAccessFile != null) {
                                randomAccessFile.close();
                            }
                            if (connection != null) {
                                connection.disconnect();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (state == Download_State.Downloading) { // 此时此线程的下载任务正常完成(没有被人为或异常中断)
                            File file = new File(filePath + "/" + fileNameTmp);
                            file.renameTo(new File(filePath + "/" + fileName));
                        }
                        if (state != Download_State.Pause) {
                            compeleted();
                        }
                    }
                    threads.remove(this);
                }
            }
        }
    }

    一个DownloadHttpTool的实例表示一个下载任务,一个下载任务中可以有多个下载线程,可以通过修改常量THREAD_COUNT的方式修改一个下载任务的下载线程数。文件的保存路径是在sdcard中的meiriq-download文件夹,也可以修改到其他路径。
    此类中在下载开始的时候首先会执行ready()方法获取文件相关的信息,之后执行startDownload()开启下载线程执行下载。
    下载时使用HttpURLConnection类的setRequestProperty方法指定请求头字段实现文件的随机下载(下载从某一个点开始到某一个点结束之际的内容),使用RandomAccessFile实现文件的随机访问(可以从某一个点开始写入数据)。
    为了保证下载速度,写入数据库的操作并不是每次写入文件之后都执行,而是在下载出现异常或者暂停等操作之后才写入数据库。所有下载任务全部结束后执行关闭数据流等操作。

    DownloadUtil.java

    package com.example.test;
    
    import java.io.File;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    
    import android.content.Context;
    import android.os.Handler;
    import android.os.Message;
    
    import com.example.test.DownloadHttpTool.DownloadComplated;
    
    /**
     * 将下载方法封装在此类 提供开始、暂停、删除以及重置的方法。<br>
     * 通过修改常量{@link DownloadUtil#MAX_COUNT}可改变最大并行下载任务量
     * 
     * @author shichaohui@meiriq.com
     */
    public class DownloadUtil {
    
        private static DownloadUtil instance = null;
        private Context context = null;
        private List<String> downloadList = null;
        private Map<String, DownloadHttpTool> downloadMap = null;
        private int currentUrlIndex = -1;
        private final int MAX_COUNT = 2; // 最大并行下载量
        private int currentCount = 0; // 当前并行下载量
        private final String FLAG_FREE = "free"; // 标记downloadMap中空闲的DownloadHttpTool实例
        private OnDownloadListener onDownloadListener = null;
    
        private Handler mHandler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String url = msg.obj.toString();
                if (msg.what == 0) {
                    if (onDownloadListener != null) {
                        onDownloadListener
                                .downloadProgress(url, msg.arg2, msg.arg1);
                    }
                } else if (msg.what == 1) {
                    if (onDownloadListener != null) {
                        onDownloadListener.downloadStart(url, msg.arg1);
                    }
                } else if (msg.what == 2) {
                    onDownloadListener.downloadEnd(url);
                }
            }
    
        };
    
        private DownloadUtil(Context context) {
            this.context = context;
            downloadList = new ArrayList<String>();
            downloadMap = new HashMap<String, DownloadHttpTool>();
        }
    
        private static synchronized void syncInit(Context context) {
            if (instance == null) {
                instance = new DownloadUtil(context);
            }
        }
    
        public static DownloadUtil getInstance(Context context) {
            if (instance == null) {
                syncInit(context);
            }
            return instance;
        }
    
        /**
         * 下载之前的准备工作,并自动开始下载
         * 
         * @param context
         */
        public void prepare(String urlString) {
            downloadList.add(urlString);
            if (currentCount < MAX_COUNT) {
                start();
            } else {
                System.out.println("等待下载____" + urlString);
            }
        }
    
        /**
         * 开始下载
         */
        private synchronized void start() {
            if (++currentUrlIndex >= downloadList.size()) {
                currentUrlIndex--;
                return;
            }
            currentCount++;
            String urlString = downloadList.get(currentUrlIndex);
            System.out.println("开始下载____" + urlString);
            DownloadHttpTool downloadHttpTool = null;
            if (downloadMap.size() < MAX_COUNT) { // 保证downloadMap.size() <= 2
                downloadHttpTool = new DownloadHttpTool(context, mHandler,
                        downloadComplated);
                if (downloadMap.containsKey(urlString)) {
                    downloadMap.remove(urlString);
                }
                downloadMap.put(urlString, downloadHttpTool);
            } else {
                downloadHttpTool = downloadMap.get(FLAG_FREE);
                downloadMap.remove(FLAG_FREE);
                downloadMap.put(urlString, downloadHttpTool);
            }
            downloadHttpTool.start(urlString);
        }
    
        /** 暂停当前下载任务 */
        public void pause(String urlString) {
            paused(urlString, new Paused() {
    
                @Override
                public void onPaused(DownloadHttpTool downloadHttpTool) {
                    downloadHttpTool.pause();
                }
            });
        }
    
        /** 暂停所有的下载任务 */
        public void pauseAll() {
            // 如果需要边遍历集合边删除数据,需要从后向前遍历,否则会出异常(Caused by:
            // java.util.ConcurrentModificationException)
            String[] keys = new String[downloadMap.size()];
            downloadMap.keySet().toArray(keys);
            for (int i = keys.length - 1; i >= 0; i--) {
                pause(keys[i]);
            }
            instance = null;
        }
    
        /**
         * 恢复当前下载任务
         * 
         * @param urlString
         *            要恢复下载的文件的地址
         */
        public void resume(String urlString) {
            prepare(urlString);
        }
    
        /** 恢复所有的下载任务 */
        public void resumeAll() {
            for (Entry<String, DownloadHttpTool> entity : downloadMap.entrySet()) {
                prepare(entity.getKey());
            }
        }
    
        /** 删除当前下载任务 */
        public void delete(String urlString) {
            boolean bool = paused(urlString, new Paused() {
    
                @Override
                public void onPaused(DownloadHttpTool downloadHttpTool) {
                    downloadHttpTool.pause();
                    downloadHttpTool.delete();
                }
            });
            if (!bool) { // 下载任务不存在,直接删除临时文件
                File file = new File(DownloadHttpTool.filePath + "/"
                        + urlString.split("/")[urlString.split("/").length - 1]
                        + DownloadHttpTool.FILE_TMP_SUFFIX);
                System.out.println(file.delete());
            }
        }
    
        interface Paused {
    
            void onPaused(DownloadHttpTool downloadHttpTool);
    
        }
    
        /**
         * 暂停
         * 
         * @param urlString
         * @param paused
         * @return 下载任务是否存在的标识
         */
        private boolean paused(String urlString, Paused paused) {
            if (downloadMap.containsKey(urlString)) {
                currentCount--;
                DownloadHttpTool downloadHttpTool = downloadMap.get(urlString);
                paused.onPaused(downloadHttpTool);
                if (!downloadMap.containsKey(FLAG_FREE)) { // 保证key == FLAG_FREE的数量
                                                            // = 1
                    downloadMap.put(FLAG_FREE, downloadHttpTool);
                }
                downloadMap.remove(urlString);
                start();
                return true;
            }
            return false;
        }
    
        DownloadComplated downloadComplated = new DownloadComplated() {
    
            @Override
            public void onComplated(String urlString) {
                System.out.println("下载完成____" + urlString);
                Message msg = new Message();
                msg.what = 2;
                msg.obj = urlString;
                mHandler.sendMessage(msg);
                pause(urlString);
                // 满足此条件说明全部下载结束
                if (downloadMap.size() == 1 && downloadMap.containsKey(FLAG_FREE)) {
                    System.out.println("全部下载结束");
                }
            }
        };
    
        /** 设置下载监听 */
        public void setOnDownloadListener(OnDownloadListener onDownloadListener) {
            this.onDownloadListener = onDownloadListener;
        }
    
        /** 下载回调接口 */
        public interface OnDownloadListener {
    
            /**
             * 下载开始回调接口
             * 
             * @param url
             * @param fileSize
             *            目标文件大小
             */
            public void downloadStart(String url, int fileSize);
    
            /**
             * 下载进度回调接口
             * 
             * @param
             * @param downloadedSize
             *            已下载大小
             * @param lenth
             *            本次下载大小
             */
            public void downloadProgress(String url, int downloadedSize, int length);
    
            /**
             * 下载完成回调
             * 
             * @param url
             */
            public void downloadEnd(String url);
    
        }
    
    }

    一个单例的类封了”开始“、”暂停“、”继续“、”删除“等下载任务相关操作方法,管理所有下载任务;利用Handler实现下载进度等信息的更新;常量FLAG_FREE标识空闲下载任务;可通过修改常量MAX_COUNT的值的方式修改最大并行下载任务数。
    该类管理下载任务的方式:获取该类实例后调用prepare(String urlString)方法添加下载任务,如果没有达到最大并行下载数,则会执行start()开始下载,否则等待其他下载任务下载完成后下载;当一个任务被暂停、删除或者下载完成后执行start()开始新的下载。集合downloadMap保存所有的下载任务,最多MAX_COUNT个。当一个下载任务完成后downloadMap中对应的下载任务变为FLAG_FREE以便后来的任务重复使用,如果FLAG_FREE的任务已存在则直接删除此任务。

    MainActivity.java

    package com.example.test;
    
    import java.text.DecimalFormat;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Timer;
    import java.util.TimerTask;
    
    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.Button;
    import android.widget.ListView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    import com.example.test.DownloadUtil.OnDownloadListener;
    
    public class MainActivity extends FragmentActivity implements OnClickListener {
    
        private ListView listView = null;
        private List<String> urls = null;
        private DownloadUtil downloadUtil = null;
        private final String TAG_PROGRESS = "_progress";
        private final String TAG_TOTAL = "_total";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            listView = (ListView) findViewById(R.id.listview);
    
            urls = new ArrayList<String>();
            urls.add("http://pc1.gamedog.cn/big/game/dongzuo/102631/shenmiaotw2_yxdog.apk");
            urls.add("http://pc1.gamedog.cn/big/game/yizhi/67450/baoweiluobo_an_yxdog.apk");
            urls.add("http://pc1.gamedog.cn/big/game/yizhi/161623/zhiwudzjs2gqb_an.apk");
    
            listView.setAdapter(myAdapter);
    
            downloadUtil = DownloadUtil.getInstance(this);
    
            downloadUtil.setOnDownloadListener(new OnDownloadListener() {
    
                String text = "已下载%sM / 共%sM \n占比%s  \n下载速度%skb/s";
                DecimalFormat decimalFormat = new DecimalFormat("#.##"); // 小数格式化
                Timer timer = null;
                Map<String, DownloadingInfo> downloadingInfos = new HashMap<String, DownloadingInfo>();
    
                @Override
                public void downloadStart(String url, int fileSize) {
                    DownloadingInfo info = new DownloadingInfo();
                    info.setFileSize(fileSize);
                    downloadingInfos.put(url, info);
                    ((ProgressBar) listView.findViewWithTag(url + TAG_PROGRESS))
                            .setMax(fileSize);
                }
    
                @Override
                public synchronized void downloadProgress(String url,
                        int downloadedSize, int length) {
                    DownloadingInfo info = downloadingInfos.get(url);
                    if (info != null) {
                        ((ProgressBar) listView.findViewWithTag(url + TAG_PROGRESS))
                                .setProgress(downloadedSize);
                        ((TextView) listView.findViewWithTag(url + TAG_TOTAL)).setText(String.format(
                                text,
                                decimalFormat
                                        .format(downloadedSize / 1024.0 / 1024.0),
                                decimalFormat.format(info.getFileSize() / 1024.0 / 1024.0),
                                (int) (((float) downloadedSize / (float) info
                                        .getFileSize()) * 100) + "%", info
                                        .getKbps()));
                        info.setSecondSize(info.getSecondSize() + length);
                    }
                    if (timer == null) {
                        timer = new Timer();
                        timer.schedule(new TimerTask() {
    
                            @Override
                            public void run() {
                                DownloadingInfo info = null;
                                for (Entry<String, DownloadingInfo> entry : downloadingInfos
                                        .entrySet()) {
                                    info = entry.getValue();
                                    if (info != null) {
                                        info.setKbps(decimalFormat.format(info
                                                .getSecondSize() / 1024.0));
                                        info.setSecondSize(0);
                                    }
                                }
                            }
                        }, 0, 1000);
                    }
                }
    
                @Override
                public void downloadEnd(String url) {
                    DownloadingInfo info = downloadingInfos.get(url);
                    if (info != null) {
                        ((ProgressBar) listView.findViewWithTag(url + TAG_PROGRESS))
                                .setProgress(info.getFileSize());
                        ((TextView) listView.findViewWithTag(url + TAG_TOTAL))
                                .setText(String.format(
                                        text,
                                        decimalFormat.format(info.getFileSize() / 1024.0 / 1024.0),
                                        decimalFormat.format(info.getFileSize() / 1024.0 / 1024.0),
                                        "100%", info.getKbps()));
                        downloadingInfos.remove(url);
                    }
                }
    
            });
    
        }
    
        BaseAdapter myAdapter = new BaseAdapter() {
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
    
                Holder holder = null;
                if (convertView == null) {
                    convertView = LayoutInflater.from(MainActivity.this).inflate(
                            R.layout.list_item, null);
                    holder = new Holder();
                    holder.tv_url = (TextView) convertView.findViewById(R.id.url);
                    holder.progressBar = (ProgressBar) convertView
                            .findViewById(R.id.progressBar);
                    holder.textView_total = (TextView) convertView
                            .findViewById(R.id.textView_total);
                    holder.button_start = (Button) convertView
                            .findViewById(R.id.button_start);
                    holder.button_pause = (Button) convertView
                            .findViewById(R.id.button_pause);
                    holder.button_resume = (Button) convertView
                            .findViewById(R.id.button_resume);
                    holder.button_delete = (Button) convertView
                            .findViewById(R.id.button_delete);
    
                    convertView.setTag(holder);
    
                    setClick(holder);
    
                } else {
                    holder = (Holder) convertView.getTag();
                }
    
                holder.tv_url.setText(urls.get(position));
    
                holder.progressBar.setTag(urls.get(position) + TAG_PROGRESS);
                holder.textView_total.setTag(urls.get(position) + TAG_TOTAL);
                holder.button_start.setTag(urls.get(position));
                holder.button_pause.setTag(urls.get(position));
                holder.button_resume.setTag(urls.get(position));
                holder.button_delete.setTag(urls.get(position));
    
                return convertView;
            }
    
            private void setClick(Holder holder) {
                holder.button_start.setOnClickListener(MainActivity.this);
                holder.button_pause.setOnClickListener(MainActivity.this);
                holder.button_resume.setOnClickListener(MainActivity.this);
                holder.button_delete.setOnClickListener(MainActivity.this);
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public Object getItem(int position) {
                return urls.get(position);
            }
    
            @Override
            public int getCount() {
                return urls.size();
            }
    
            class Holder {
                TextView tv_url = null;
                ProgressBar progressBar = null;
                TextView textView_total = null;
                Button button_start = null;
                Button button_pause = null;
                Button button_resume = null;
                Button button_delete = null;
            }
        };
    
        @Override
        public void onClick(View view) {
            String url = view.getTag() == null ? "" : view.getTag().toString();
            switch (view.getId()) {
            case R.id.button_start:
                downloadUtil.prepare(url);
                break;
            case R.id.button_pause:
                downloadUtil.pause(url);
                break;
            case R.id.button_resume:
                downloadUtil.resume(url);
                break;
            case R.id.button_delete:
                downloadUtil.delete(url);
                break;
    
            default:
                break;
            }
        }
    
    }

    使用ListView展示待下载任务列表。使用List<String>集合urls保存下载地址。使用View.setTag(url+TAG_PROGRESS/TAG_TOTAL)的方式标记各个任务对应的View。在显示数据的时候使用findViewWithTag(url+TAG_PROGRESS/TAG_TOTAL)的方式取得对应的View;使用Timer定时器,每隔1s刷新一次下载速度。
    activity_main.xml

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <ListView 
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>

    list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" >
    
        <TextView
            android:id="@+id/url"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/url" />
    
        <TextView
            android:id="@+id/textView_total"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/progressBar"
            android:layout_margin="10dp"
            android:lines="3"
            android:text="下载进度" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/textView_total" >
    
            <Button
                android:id="@+id/button_start"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="开始"
                android:textSize="12sp" />
    
            <Button
                android:id="@+id/button_pause"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="暂停"
                android:textSize="12sp" />
    
            <Button
                android:id="@+id/button_resume"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="继续"
                android:textSize="12sp" />
    
            <Button
                android:id="@+id/button_delete"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="删除"
                android:textSize="12sp" />
        </LinearLayout>
    
    </RelativeLayout>

    最后添加连接网络和操作sdcard的权限:

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

    此代码是使用数据库和RandomAccessFile类实现断点下载,除此方法外,还可以用临时文件的方式实现断点下载,具体方法:同以上代码使用HttpURLConnection和setRequestProperty方法实现文件的部分下载,然后每一部分保存为一个临时文件,待全部下载完成后,将所有相关临时文件写入到一个文件,并删除临时文件即可。

    展开全文
  • 作为android初学者,最近把疯狂android讲义和疯狂Java讲义看了一遍,看到书中介绍的知识点非常多,很难全部记住,为了更好的掌握基础知识点,我将开发一个网络音乐播放器-EasyMusic来巩固下,也当作是练练手。...
  • 最近在做一个单线程多任务的断点排队下载的功能,网上确实有很多这样的demo。但是呢我发现大部分网上的demo都是很些不完整的要么就是有缺陷的,可能是我还没找到。今天我给大家带来的一个功能完整的并且可以多界面...
  • 这里的demo是一个通过下载地址下载图片到手机本地,并在Activity中展示这种图片的例子。 布局 &amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&...
  • 最近的项目是一个与音乐相关的App,...最初的思路便是任务队列,单线程顺序执行,一个文件接着一个文件进行下载。之前看过AsyncTask的部分源码,其设计与我的想法类似,于是便借鉴着AsyncTask的源码,实现了一个特别
  • Android实现网络下载一(单任务下载–支持断点续传) 说起下载,在Android的一些类似游戏宝的项目中会频繁使用,比如说需求要下载一个apk文件,并且要支持暂停、继续等要求。当然在GitHib上也有一些优秀的下载框架...
  • 安卓期末大作业Android studio,(免积分下载)包含项目源码,项目报告书,任务书,App等等应有尽有,项目导入即可使用。 1.用户点击加号按钮输入单词,键盘会自动弹出并焦点在输入框,用户输入英文和中文释义,...
  • 转载:http://blog.csdn.net/ausboyue/article/details/71155831丨版权说明 :《OkHttp实现多线程断点续传下载,单例模式下多任务下载管理器,一起抛掉sp,sqlite的辅助吧》于当前CSDN博客和乘月网属同一原创,转载请...
  • 一个超简单的android任务列队(排队)3
  • Android网络音乐播放器 源码下载

    热门讨论 2017-05-12 13:38:38
    多线程下载-多个线程分割下载任务提高下载效率并结合数据库实现断点下载; 异步任务AsyncTask执行耗时任务-音乐的收藏(使用到数据库)操作及音乐的搜索等需要访问网络的操作; 自定义view实现圆形专辑图片,滚动...
  • Android下载管理器

    2021-05-27 04:19:01
    DownloadManagerAndroid下载管理器1、可实现下载、暂停、取消、完成、失败、队列等待等状态的监听2、可断点续传3、基于okhttp的下载管理器,可实现单个页面.../*** 添加下载任务** @param url 下载请求的网址* @para...
  • 安卓7.0正式版安装包下载

    千次阅读 2021-05-28 02:03:48
    安卓7.0正式版安装包是一款针对低端手机打造的安卓系统。目前运行Android 7.0开发者预览版的设备能够第一时间收到推送,而还处于Android 6.0的设备则需要更长时间,用户可以通过线刷的方式来让自己的手机进行升级。...
  • Introduce基于OkHttp实现的下载管理,支持多线程、断点续传等功能,Demo演示用的是RecyclerView,在Application里可设置任务并发等。实现时基本无别的臃肿代码,可以方便修改使用。DemoScreenshotUseGradlecompile '...
  • * 系统下载下载 * * @author Administrator * @date 2019/4/1 */ public class SystemDownManagerUtils { private static DownloadManager downloadManager; private static SystemDownLoadComplete...
  • Android使用 WorkManager 调度任务

    千次阅读 2020-04-22 19:25:16
    WorkManager属于Android Jetpack的一部分,通过WorkManager API可以轻松第调度可延迟的任务,即使是那些在应用退出或者设备重启时仍需要运行的任务。 WorkManager的主要功能 最高可向后兼容到 API 14 在 API 23 及...
  • 您可以对诸如编辑,删除,标记为完成,激活和停用之类的任务执行基本操作。 有关更多信息,请参考。 #屏幕截图 什么是新的 不适用 下载下载最新的apk。 路线图 该项目没有有效的路线图。 # 执照 Copyright ...
  • Android任务断点下载

    千次阅读 2015-07-02 15:34:26
    项目中有一个多任务下载模块,使用的是第三方的一个下载模块Afinal框架,感觉也不是很好用,所以呢,就自己打算自己写一个,写了才发现,为什么到目前为止还没有一个很好用的下载SDK,说多了都是泪……………………....
  • 2.虽然原来的设计是采用多线程断点续传的设计,但打了一下日志发现其实下载任务都是在同一个线程下串行执行,并没有起到加快下载速度的作用。 考虑到原来的代码并不复杂,因此对这部分下载组件进行了重写。这里记录...
  • 更好的Android多线程下载框架

    千次阅读 2017-03-30 15:28:52
    */概述为什么是更好的Android多线程下载框架呢,原因你懂的,广告法嘛!本篇我们我们就来聊聊多线程下载框架,先聊聊我们框架的特点: 多线程 多任务 断点续传 支持大文件 可以自定义下载数据库
  • Android OSS下载文件

    2021-04-08 15:32:53
    近期,公司要求使用OSS下载文件,优化下载模块 首先,想要使用OSS,必须下载其相关依赖 implementation 'com.aliyun.dpa:oss-android-sdk:2.9.5' // implementation project(':oss-android-sdk') 依赖下载成功后...
  • 毕业设计-基于安卓Android学生考勤信息管理系统.zip 2 毕业设计 毕业设计-安卓Android教务选课成绩管理系统.zip 3 毕业设计 114基于Android的理财记账本20141215+论文+需求文档+演示视频.zip.zip...
  • ls,cat ,grep,sed之类的完全可以完成这个任务。可是在电视 上找不到sed,甚至head,tail也没有,这下彻底歇菜。对安卓不熟悉,也不知道有何替代工具。甚至想去安装busybox ,就是这一想法,差点直接下载apk安装了,尝试...
  • 上个版本手机迅雷正式同步迅雷7的下载任务云列表,雷友们体验后纷纷吐槽,最大的槽点自然是删除不了下载任务记录。其实,每个版本的需求都是经过产品经理们多番讨论权衡后排期的,这个删除的功能排进了2.10.2版的...
  • android的一个下载框架Aria

    千次阅读 2017-08-29 14:43:51
    Aria项目源于15年工作中遇到的一个文件下载管理的需求,当时被下载折磨的痛不欲生,从那时起便萌生了编写一个简单易用,稳当高效的下载框架,aria经历了1.0到3.0的开发,算是越来越接近当初所制定的目标
  • Android 使用 DownloadManager 管理系统下载任务的方法

    千次阅读 多人点赞 2014-07-17 09:46:20
    本文详细介绍Android应用管理设备下载状态信息的方法
  • Android中有一个ActivityThread类,这个类代表应用程序的主线程;Android在打开APP时会首先调用ActivityThread中的main方法,也就是APP进程的启动点; public static void main(String[] args) { ... Looper....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,568
精华内容 9,827
关键字:

安卓怎么删除正在下载的任务