不管是开发android应用程序还是java应用程序,异步任务都是经常用到的,尤其是android本身做为线程不安全,只要稍微耗时的操作都要用到异步任务,而无论是java还是android通用的异步任务开发以前无非就是利用Thread和Runnable来实现, android系统本身还有属于他自己的AsyncTask去专门处理异步任务.但其实这些都不是最高效的异步任务处理方法,尤其是任务有多个的情况下,以上几种方法都需要后台不停的去创建新的线程,并且要求我们自己去管理好线程的生命周期,否则很容易造成内存泄露.
而从 jdk1.5以后,我们可以使用线程池去高效的创建多个异步任务的同时,还不需要自己去管理线程的生命周期了.我们只需要专注于异步任务的业务逻辑代码和在适当的时候将线程池整体关闭,可以大大的提高整个应用的性能.
下面就来先看下线程池相关的一些经常要用到的类,这些类都包括:Executor, ExecutorService, AbstractExecutorService,ThreadPoolExecutorService,ScheduledExecutirService, Callable, Runnable等,常用到的相关类就是以上几个.下面我们来分析一下这些类的作用.
1.Executor, 一个接口,只提供了一个方法executor(Runnable command);提供了线程池最基本的功能,执行异步任务.
2.ExecutorService,也是接口,继承了Executor接口,并扩展了一些线程池应该具有的生命周期方法,源码(部分)如下:
public interface ExecutorService extends Executor { /** * 作用:关闭线程池,不会停止已有的任务,但不能再向其中添加新任务. */ void shutdown(); /** * * @return list of tasks that never commenced execution * 作用:立马关闭整个线程池,池中任务也一并停止. */ List<Runnable> shutdownNow(); boolean isShutdown(); /** * 作用:向线程池中添加一个Callable任务. */ <T> Future<T> submit(Callable<T> task); /** * <pre name="code" class="java"> * 作用:向线程池中添加一个Runnable任务.*/ <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); } 3.AbstractExecutorService,线程池抽象类,提供了所有线程池类的基本实现,部分代码如下:
public abstract class AbstractExecutorService implements ExecutorService { /** * 将Runnable任务转化为RunnableFutrue任务 * */ protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } <pre name="code" class="java"> /** * 将Callable任务转化为RunnableFutrue任务 * */ protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } <pre name="code" class="java"> /** * 向线程池中添加一个Runnable任务,会在内部将任务转化为RunnableFuture任务 * */ public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } <pre name="code" class="java"><pre name="code" class="java"><pre name="code" class="java"> /** * 向线程池中添加一个Callable任务,会在内部将任务转化为RunnableFuture任务 * */public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }} 通过阅读源码可以知道:线程中处理的任务最终都是FutureTask类型的.
3.Executors,通过提供一系列的static方法,创建不同类型的线程池.源码不再拷贝.
以上这些类基本构成了java线程池的整个框架,利用这几个类,可以使我们轻松的实现线程池的异步任务.下面以一个Demo来讲解基本的用法.
最近有一个需求是这样的:从服务器下载体积较大的zip包到本地客户端中.并通过Notifycation通知用户文件下载的进度.思路:要从服务器下载东西,启动一个专门用来下载的Service,在Service中通过线程池开启异步下载任务并创建下载过程中不同状态的回调接口,用以通知用户下载进度.本Demo中为了实现上的方便,先实现了将文件从一个路径拷贝到另一个路径来模拟从服务器下载文件.(原理是一样的,下载文件即相当于从服务器拷贝一个文件到本地路径).
下面看一下整个工程的结构:
各个类的作用:
1. CopyTask,专门用来实现文件拷贝功能的类,没有其它任务业务.代码如下:
package copy; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; /** * @author rzq * @functuion android SD卡文件复制任务类 */ public class CopyTask implements Runnable { /** * 要拷贝的文件路径 */ private String sourceFilePath; private File sourceFile; /** * 目的地址文件 */ private String destationFilePath; private File destationFile; /** * 复制过程状态回调 */ private IDownloadListener downlaodListener; private long fileContentLength; private long currentLenght = 0; public CopyTask(String sourceFilePath, String destationFilePath, IDownloadListener downloadListener) { this.sourceFilePath = sourceFilePath; this.destationFilePath = destationFilePath; this.downlaodListener = downloadListener; } private boolean prepare() { sourceFile = new File(sourceFilePath); fileContentLength = sourceFile.length(); /** * 文件长度大于0,则准备好了 */ if (fileContentLength > 0) { destationFile = new File(destationFilePath); downlaodListener.onPrepared(fileContentLength); return true; } return false; } @Override public void run() { if (prepare()) { /** * 文件准备好后开始拷贝 */ RandomAccessFile randomAccessFile = null; FileInputStream in = null; try { in = new FileInputStream(sourceFile); byte[] buffer = new byte[2048]; int length = -1; randomAccessFile = new RandomAccessFile(destationFile, "rwd"); while (((length = in.read(buffer)) != -1)) { randomAccessFile.write(buffer, 0, length); currentLenght += length; downlaodListener.onProgressChanged((int) (currentLenght / fileContentLength * 100)); if (currentLenght == fileContentLength) { downlaodListener.onFinished((int) currentLenght); } } } catch (FileNotFoundException e1) { downlaodListener.onFailure(); e1.printStackTrace(); } catch (IOException e2) { downlaodListener.onFailure(); e2.printStackTrace(); } finally { try { in.close(); randomAccessFile.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }2.CopyManager类,用来负责创建线程池并向其中添加任务.代码如下:
package copy; import java.lang.ref.WeakReference; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; public class CopyManager { private static CopyManager manager; private CopyTask copyTask; private ThreadPoolExecutor threadPool; private CopyManager() { threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); } static { manager = new CopyManager(); } public static CopyManager getInstance() { return manager; } public void startCopyFile(String sourceFile, String destiationFile, IDownloadListener listener) { copyTask = new CopyTask(sourceFile, destiationFile, listener); Future<?> request = threadPool.submit(copyTask); new WeakReference<Future<?>>(request); } }3.IDownloadListener,监听不同状态下不同的回调执行.代码如下:
package copy; public interface IDownloadListener { /** * 开始请求的回调 */ public void onStarted(); /** * 请求成功,下载前的准备回调 * * @param contentLength * 文件长度 * @param downloadUrl * 下载地址 */ public void onPrepared(long contentLength); /** * 正在下载,更新进度的回调 * * @param progress * 当前下载进度 * @param completeSize * 已下载完成长度 * @param downloadUrl * 下载地址 */ public void onProgressChanged(int progress); /** * 下载过程中暂停的回调 * * @param completeSize * @param downloadUrl */ public void onPaused(int progress, int completeSize); /** * 下载完成的回调 */ public void onFinished(int completeSize); /** * 下载失败的回调 */ public void onFailure(); }4.CopyService,文件下载服务,通过调用CopyManager去开始下载任务.不再贴源码,稍后会将整个Demo源码上传.
Demo下载