不管是開發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下載