android线程池介绍

来源:互联网 发布:防盗门锁芯规格 知乎 编辑:程序博客网 时间:2024/06/04 19:15

一. 使用ThreadPoolExecutor封装线程池管理类

public class ThreadPoolManager {    private static ThreadPoolManager mInstance = new ThreadPoolManager();    private ThreadPoolExecutor executor;    private int corePoolSize;//核心线程池数量,表示能够同时执行的任务数量    private int maximumPoolSize;//最大线程池数量,    private long keepAliveTime = 1;//等待任务的存活时间    private TimeUnit unit = TimeUnit.HOURS;//存活时间的时间单位    public static ThreadPoolManager getInstance(){        return mInstance;    }    private ThreadPoolManager(){        //核心线程池数量的计算规则:当前设备的可用处理器核心数*2 + 1,能够让CPU得到最大效率的发挥;        corePoolSize = Runtime.getRuntime().availableProcessors()*2+1;        maximumPoolSize = corePoolSize;//虽然用不到,但是不能是0,否则报错        //线程池机制:领工资的机制        executor = new ThreadPoolExecutor(                corePoolSize, //3                maximumPoolSize,//5,最大的线程池数量                 keepAliveTime,                 unit,                 new LinkedBlockingQueue<Runnable>(),//缓冲队列,超出核心线程池的任务会被放入缓存队列中等待                Executors.defaultThreadFactory(),//创建线程的工厂类                new ThreadPoolExecutor.AbortPolicy()//当超出最大线程池数量的时候,则拒绝执行                );    }    /**     * 往线程池中添加任务     * @param r     */    public void execute(Runnable r){        if(r!=null){            executor.execute(r);        }    }    /**     * 从线程池中移除任务     * @param r     */    public void remove(Runnable r){        if(r!=null){            executor.remove(r);        }    }}

二. 定义DownloadManager,定义下载状态常量

public class DownloadManager {    public static final int STATE_NONE = 0;//未下载的状态    public static final int STATE_DOWNLOADING = 1;//下载中的状态    public static final int STATE_PAUSE = 2;//暂停的状态    public static final int STATE_WAITING = 3;//等待中的状态,任务对象已经创建,但是run方法没有执行    public static final int STATE_FINISH = 4;//下载完成的状态    public static final int STATE_ERROR = 5;//下载出错的状态}

三. 将DownloadManager设计为单例模式

private static DownloadManager mInstance = new DownloadManager();public static DownloadManager getInstance(){    return mInstance;}private DownloadManager(){}

四. 定义下载相关的方法,如download方法,pause方法等

public void download(){}public void pause(){}public void installApk(){}public void registerDownloadObserver(DownloadObserver downloadObserver){    if(!observerList.contains(downloadObserver)){        observerList.add(downloadObserver);    }}/** * 从集合中移除下载观察者 * @param downloadObserver */public void unregisterDownloadObserver(DownloadObserver downloadObserver){    if(observerList.contains(downloadObserver)){        observerList.remove(downloadObserver);    }}/** * 下载状态和进度改变的监听器 * @author Administrator * */public interface DownloadObserver{    /**     * 下载状态改变的回调     */    void onDownloadStateChange(DownloadInfo downloadInfo);    /**     * 下载进度改变的回调     */    void onDownloadProgressChange(DownloadInfo downloadInfo);}

五. 定义DownloadInfo类,用来封装每个下载任务的数据

/** * 封装的是下载任务相关的数据,包括标识,进度,状态,文件保存路径等 *  */public class DownloadInfo {    private long id;// 下载任务的唯一标识    private int state;// 下载状态    private long currentLength;// 已经下载的长度    private long size;// 总大小    private String downloadUrl;// 下载地址    private String path;// apk文件的保存的绝对路径    /**     * 使用AppInfo创建DownloadInfo     * @return     */    public static DownloadInfo create(AppInfo appInfo) {        DownloadInfo downloadInfo = new DownloadInfo();        downloadInfo.setId(appInfo.getId());        downloadInfo.setSize(appInfo.getSize());        downloadInfo.setDownloadUrl(appInfo.getDownloadUrl());        downloadInfo.setState(DownloadManager.STATE_NONE);// 初始化状态        downloadInfo.setCurrentLength(0);        // /mnt/sdcard/包名/download/有缘网.apk        downloadInfo.setPath(DownloadManager.DOWNLOAD_DIR + File.separator                + appInfo.getName() + ".apk");        return downloadInfo;    }}

六. 实现download方法

  • 先根据app的id获取对象的任务信息,即DownloadInfo,如果没有则通过静态方法创建,然后存入map中

    DownloadInfo downloadInfo = downloadInfoMap.get((int) appInfo.getId());if(downloadInfo==null){    //说明该任务从来没有下载过    downloadInfo = DownloadInfo.create(appInfo);    //将downloadInfo存入downloadInfoMap中    downloadInfoMap.put((int) downloadInfo.getId(), downloadInfo);}
  • 获取state,判断state是否可以进行下载,如果可以则创建DownloadTask,并交给线程池执行

    int state = downloadInfo.getState();if(state==STATE_NONE || state==STATE_PAUSE || state==STATE_ERROR){    //说明可以进行下载了,那么就可以创建DownloadTask,交给线程池管理    DownloadTask downloadTask = new DownloadTask(downloadInfo);    //更新下载任务对应的state    downloadInfo.setState(STATE_WAITING);//更新状态为等待中    //通知监听器状态更改了    notifyDownloadStateChange(downloadInfo);    //3.将DownloadTask交给线程池管理    ThreadPoolManager.getInstance().execute(downloadTask);}
  • 实现DownloadTask,在run方法的开始就将状态置为下载中

    //4.run方法执行就将state设置downloadingdownloadInfo.setState(STATE_DOWNLOADING);//更新状态为等待中//通知监听器状态更改notifyDownloadStateChange(downloadInfo);
  • 判断下载类型,进行相应的下载

    //5.判断下载类型:a.从头下载     b.断点下载HttpUtil.HttpResult httpResult = null;File file = new File(downloadInfo.getPath());if(!file.exists() || file.length()!=downloadInfo.getCurrentLength()){    //需要从头下载的情况    file.delete();//删除无效文件    downloadInfo.setCurrentLength(0);//清空currentLength    String url = String.format(Url.Download,downloadInfo.getDownloadUrl());    httpResult = HttpEngine.getInstance().download(url);}else {    //需要断点下载的情况    String url = String.format(Url.Break_Download,downloadInfo.getDownloadUrl(),downloadInfo.getCurrentLength());    httpResult = HttpEngine.getInstance().download(url);}
  • 下载类使用HttpEngine,里面封装的是HttpClient, 重在理解下载逻辑的实现思路;

  • 获取到HttpResult后,则可以开始读取流下载文件了,并且读取的过程中要注意通知进度更新

    //6.读取流,写入文件    if(httpResult!=null ){        //说明成功的请求到了文件数据        InputStream is = httpResult.getInputStream();        FileOutputStream fos = null;        try {            fos = new FileOutputStream(file,true);            byte[] buffer = new byte[1024*8];//8k的缓冲区            int len = -1;            while((len=is.read(buffer))!=-1 && downloadInfo.getState()==STATE_DOWNLOADING){                fos.write(buffer, 0, len);//写文件                //更新currentLength                downloadInfo.setCurrentLength(downloadInfo.getCurrentLength()+len);                //通知监听器下载进度更新                notifyDownloadProgressChange(downloadInfo);            }        } catch (Exception e) {            e.printStackTrace();            Log.e(TAG, "run: "+e.getLocalizedMessage() );            //属于下载失败的情况            processDownloadError(file);        }finally{            //关闭链接和流            httpResult.close();            try {                if(fos!=null)fos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        //7.代码走到这里:a.下载完成      b.暂停        if(file.length()==downloadInfo.getSize() && downloadInfo.getState()==STATE_DOWNLOADING){            //说明下载完成了            downloadInfo.setState(STATE_FINISH);            notifyDownloadStateChange(downloadInfo);        }else if (downloadInfo.getState()==STATE_PAUSE) {            notifyDownloadStateChange(downloadInfo);        }    }else {        //说明请求文件数据失败        processDownloadError(file);    }

七. 实现暂停方法,取出DownloadTask,将其状态更改为暂停即可

/** * 暂停的方法 */public void pause(AppInfo appInfo){    DownloadInfo downloadInfo = getDownloadInfo(appInfo);    if(downloadInfo!=null){        //将当前downloadINfo的state设置为pause        downloadInfo.setState(STATE_PAUSE);//更改状态        notifyDownloadStateChange(downloadInfo);    }}

八. 实现安装apk的方法

/** * 安装app的方法 */public void installApk(AppInfo appInfo){    DownloadInfo downloadInfo = getDownloadInfo(appInfo);    if(downloadInfo!=null){        /*<action android:name="android.intent.action.VIEW" />        <category android:name="android.intent.category.DEFAULT" />        <data android:scheme="content" />        <data android:scheme="file" />        <data android:mimeType="application/vnd.android.package-archive" />*/        Intent intent = new Intent(Intent.ACTION_VIEW);        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//开启新的任务栈来存放新的Activity        intent.setDataAndType(Uri.parse("file://"+downloadInfo.getPath()),"application/vnd.android.package-archive");        GooglePlayApp.context.startActivity(intent);    }}

九. 编写AppDownloadHolder类,完成下载功能

  • 在initHolderView中注册下载监听器

    // 注册下载监听器DownloadManager.getInstance().registerDownloadObserver(this);
  • 处理点击事件,点击的时候根据当前的state进行相应的操作

    public void onClick(View v) {switch (v.getId()) {case R.id.btn_download:    DownloadInfo downloadInfo = DownloadManager.getInstance()            .getDownloadInfo(appInfo);    if (downloadInfo == null) {        // 说明从来没有下载过,那么就去下载        DownloadManager.getInstance().download(appInfo);    } else {        // 说明下载过,那么应该根据state来判断        if (downloadInfo.getState() == DownloadManager.STATE_DOWNLOADING                || downloadInfo.getState() == DownloadManager.STATE_WAITING) {            // 应该暂停            DownloadManager.getInstance().pause(appInfo);        } else if (downloadInfo.getState() == DownloadManager.STATE_PAUSE                || downloadInfo.getState() == DownloadManager.STATE_ERROR) {            // 应该继续下载,或者重新下载            DownloadManager.getInstance().download(appInfo);        } else if (downloadInfo.getState() == DownloadManager.STATE_FINISH) {            // 应该安装            DownloadManager.getInstance().installApk(appInfo);        }    }    break;}

    }

  • 根据下载状态和下载进度的更新UI

    /** * 下载进度改变 */@Overridepublic void onDownloadProgressChange(final DownloadInfo downloadInfo) {    if(appInfo==null || appInfo.getId()!=downloadInfo.getId()){        //如果当前的app和正在下载的app不是同一个        return;    }    // 获取百分比 12.3333    float percent = downloadInfo.getCurrentLength() * 100f            / downloadInfo.getSize();    // 将进度设置给进度条    pb_progress.setProgress((int) percent);    // 将百分比设置给下载按钮    btn_download.setBackgroundResource(0);// 移除按钮的背景    btn_download.setText((int) percent + "%");}@Overridepublic void onDownloadStateChange(final DownloadInfo downloadInfo) {    updateDownloadState(downloadInfo);}/** * 根据当前的下载状态来更新UI * @param downloadInfo */public void updateDownloadState(final DownloadInfo downloadInfo) {    if(appInfo==null || appInfo.getId()!=downloadInfo.getId()){        //如果当前的app和正在下载的app不是同一个        return;    }    switch (downloadInfo.getState()) {    case DownloadManager.STATE_PAUSE:        btn_download.setText("继续下载");        // 显示下载的进度条        // 获取百分比 12.3333        float percent = downloadInfo.getCurrentLength() * 100f                / downloadInfo.getSize();        // 将进度设置给进度条        pb_progress.setProgress((int) percent);        btn_download.setBackgroundResource(0);// 移除按钮的背景        break;    case DownloadManager.STATE_FINISH:        btn_download.setText("安装");        break;    case DownloadManager.STATE_WAITING:        btn_download.setText("等待中");        break;    case DownloadManager.STATE_ERROR:        btn_download.setText("出错,重下");        break;    }}
  • 在绑定数据的时候,需要根据当前的state初始化UI

    public void bindData(AppInfo appInfo) {    this.appInfo = appInfo;    // 刚进来的时候如果是暂停的则会导致UI有错误,需要手动获取当前的state进行更新    DownloadInfo downloadInfo = DownloadManager.getInstance()            .getDownloadInfo(appInfo);    if (downloadInfo != null) {        updateDownloadState(downloadInfo);    }}



0 0