android 单线程多任务断点排队下载(支持多界面刷新)

本文介绍了如何实现Android单线程多任务断点下载,并使用线程池进行排队管理,同时讲解了数据库、文件操作、监听回调以及下载管理器的关键部分。在下载过程中,利用HTTP的Range属性支持断点续传,确保下载的可靠性。文章还强调了错误处理和回调更新界面的重要性。
摘要由CSDN通过智能技术生成
最近在做一个单线程多任务的断点排队下载的功能,网上确实有很多这样的demo。但是呢我发现大部分网上的demo都是很些不完整的要么就是有缺陷的,可能是我还没找到。今天我给大家带来的一个功能完整的并且可以多界面刷新,就比如:我当前界面点了下载放后台下载了,退出了当前界面在进来网上很多这样demo都没做继续更新界面。并且还做了排队。

首先我们先制定下计划:

1、我们做的是断点下载,断点下载肯定是需要记录当前下载的进度和文件总大小的。这个当然是放在数据库里面最好,当然肯定会是异步来操作数据库,这里我们就需要用到线程锁synchronized来控制了。

2、多任务是指可以多个任务一起下载,但是我们是有排队机制的,所以当前任务只是一个任务在下载,其他的按点击顺序排队执行,这里我们就需要写一个下载管理器来管理整个下载团队,当暂停和取消都是需要把队列排队给去掉的,当然我这里是用的线程池(ThreadPoolExecutor)来管理线程的。

3、还需要做到多界面刷新,我这里是通过注册一个下载监听来做的,所有的监听都放在map集合里面,已id为key来进行保存,每次需要回调监听的时候都是通过map里面去找,所以销毁的时候需要去注销下所有的监听,然而每次去新的界面只要注册了下载监听,我又可以再map里面找到你新注册的监听从而来进行新的回调。

话不多说,请看效果:

这里写图片描述


接下来我们来分析代码:

首先我们需要创建数据库、创建表、创建文件夹、所需的操作表的代码、以及下载所需要的Bean(这种初始化操作就不在这里详细做描述)



现在我们说重点,线程池的管理

由于我们是单线程,所以线程池里面同时执行的线程只需为1就可以,(意思就是说当前线程执行的只有1条线程,当前的线程还没执行完毕就把后面添加进来的线程进行有序排序<先来后到顺序>)。并且还可以拿到排队中并还没执行的线程,这样我们就可以做很多事了。

package com.down.executors;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;

/**
 *  线程池工具
 */
public class DownLoadExecutor {
   

    /** 请求线程池队列,同时允许1个线程操作 */
    private static ThreadPoolExecutor mPool ;

    //当线程池中的线程小于mCorePoolSize,直接创建新的线程加入线程池执行任务
    private static final int mCorePoolSize = 1;
    //最大线程数
    private static final int mMaximumPoolSize = 1;
    //线程执行完任务后,且队列中没有可以执行的任务,存活的时间,后面的参数是时间单位
    private static final long mKeepAliveTime = 5L;

    /** 执行任务,当线程池处于关闭,将会重新创建新的线程池 */
    public synchronized static void execute(Runnable run) {
        if (run == null) {
            return;
        }
        if (mPool == null || mPool.isShutdown()) {
            //参数说明
            //当线程池中的线程小于mCorePoolSize,直接创建新的线程加入线程池执行任务
            //当线程池中的线程数目等于mCorePoolSize,将会把任务放入任务队列BlockingQueue中
            //当BlockingQueue中的任务放满了,将会创建新的线程去执行,
            //但是当总线程数大于mMaximumPoolSize时,将会抛出异常,交给RejectedExecutionHandler处理
            //mKeepAliveTime是线程执行完任务后,且队列中没有可以执行的任务,存活的时间,后面的参数是时间单位
            //ThreadFactory是每次创建新的线程工厂
            mPool = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new AbortPolicy());
        }
        mPool.execute(run);
    }

    /** 取消线程池中某个还未执行的任务 */
    public synchronized static boolean cancel(Runnable run) {
        if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
            return mPool.getQueue().remove(run);
        }else{
            return false;
        }
    }

    /** 查看线程池中是否还有某个还未执行的任务 */
    public synchronized static boolean contains(Runnable run) {
        if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
            return mPool.getQueue().contains(run);
        } else {
            return false;
        }
    }

    /** 立刻关闭线程池,并且正在执行的任务也将会被中断 */
    public static void stop() {
        if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
            mPool.shutdownNow();
        }
    }

    /** 平缓关闭单任务线程池,但是会确保所有已经加入的任务都将会被执行完毕才关闭 */
    public synchronized static void shutdown() {
        if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
            mPool.shutdownNow();
        }
    }
}

这里是取名为观察者,也就是监听回调

package com.down.executors;

import com.down.bean.DownLoadBean;

/**
 *  观察者
 */
public interface DownLoadObserver {
   
    /**准备下载*/
    void onPrepare(DownLoadBean bean);
    /** 开始下载 */
    void onStart(DownLoadBean bean);
    /** 下载中 */
    void onProgress(DownLoadBean bean);
    /** 暂停 */
    void onStop(DownLoadBean bean);
    /** 下载完成 */
    void onFinish(DownLoadBean bean);
    /** 下载失败 */
    void onError(DownLoadBean bean);
    /** 删除成功 */
    void onDelete(DownLoadBean bean);
}

现在关键部分到了,我们的下载管理器。

管理器全局操作:开始下载之前必须先注册下载监听,不然后不会回调,注册监听的时候就把监听放到map集合,开启下载的时候就把当前线程也到map集合保存,这样就简单啦,假如我需要暂停或者删除当前下载的任务,直接改变状态在下载的while循环里面直接return就可,如果是排队中的呢,则是先拿到队列中的任务线程再把线程中的bean对象里面的状态置为暂停或者删除,这样就算到它执行的时候也会直接返回(因为我的执行线程里面第一行代码就是判断是否被暂停或删除),继续执行排在他后面的任务线程。删除下载任务则是先从map集合里面通过id拿到需要删除的线程,然后把线程里面的状态改为删除,然后删除数据库,删除文件。

package com.down.executors;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.down.bean.DownLoadBean;
import com.down.uitl.DataBaseUtil;

/**
 * 下载管理器
 */
public class DownLoadManager {
   
    public static final int STATE_NONE = -1;
    /** 开始下载 */
    public static final int STATE_START = 0;
    /** 等待中 */
    public static final int STATE_WAITING = 1;
    /** 下载中 */
    public static final int STATE_DOWNLOADING = 
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值