前言##
任何一個Android 開發(fā)者對AsnycTask 都應(yīng)該不陌生;使用AsyncTask可以很方便的異步處理耗時操作暇赤;AsyncTask內(nèi)部對Handler和Thread進行了封裝心例,簡化了Handler的使用方式,使用起來非常方便鞋囊。首先看一下Android SDK 中關(guān)于AsyncTask的使用示例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
在onCreate中止后,執(zhí)行這個任務(wù)即可:
new DownloadFilesTask().execute(url1, url2, url3);
使用起來,的確是很方便(當然和當下非常流行的Retrofit、Volley相比還是有些繁瑣)译株。
但是你想過下面這些問題嗎瓜喇;為什么doInBackground就可以用來進行耗時操作悦屏?為什么onPostExecute和onProgressUpdate就可以進行UI更新里覆?是誰規(guī)定了這些方法處于UI線程的?publishProgress 傳遞的參數(shù)是怎樣來到onProgressUpdate方法中的万栅?怎樣取消一個正在執(zhí)行的耗時操作匪补?如果你自己去封裝Handler和Thread你會怎么做伞辛?
好了,帶著這些疑問夯缺,讓我們?nèi)タ纯碅syncTask的實現(xiàn)原理蚤氏。
AsyncTask的實現(xiàn)原理可以說是十分巧妙,整個代碼除去注釋僅有300多行踊兜,但功能卻非常強大竿滨,原因就是他用到了Java自帶的并發(fā)工具包 java.util.concurrent,這個包包含有一系列能夠讓 Java 的并發(fā)編程變得更加簡單輕松的類捏境。好了為了方便理解AsyncTask的實現(xiàn)原理于游,先普及一波AsyncTask中用到一些java基礎(chǔ)知識。
基礎(chǔ)知識##
阻塞隊列 BlockingQueue####
BlockingQueue 通常用于一個線程生產(chǎn)對象垫言,而另外一個線程消費這些對象的場景贰剥。下圖是對這個原理的闡述:

一個線程往里邊放,另外一個線程從里邊取的一個 BlockingQueue骏掀。
一個線程將會持續(xù)生產(chǎn)新對象并將其插入到隊列之中鸠澈,直到隊列達到它所能容納的臨界點柱告。也就是說截驮,它是有限的。如果該阻塞隊列到達了其臨界點际度,負責生產(chǎn)的線程將會在往里邊插入新對象時發(fā)生阻塞葵袭。它會一直處于阻塞之中,直到負責消費的線程從隊列中拿走一個對象乖菱。
負責消費的線程將會一直從該阻塞隊列中拿出對象坡锡。如果消費線程嘗試去從一個空的隊列中提取對象的話,這個消費線程將會處于阻塞之中窒所,直到一個生產(chǎn)線程把一個對象丟進隊列鹉勒。
鏈阻塞隊列 LinkedBlockingQueue#####
LinkedBlockingQueue 類實現(xiàn)了 BlockingQueue 接口。
LinkedBlockingQueue 內(nèi)部以一個鏈式結(jié)構(gòu)(鏈接節(jié)點)對其元素進行存儲吵取。如果需要的話禽额,這一鏈式結(jié)構(gòu)可以選擇一個上限。如果沒有定義上限,將使用 Integer.MAX_VALUE 作為上限脯倒。
LinkedBlockingQueue 內(nèi)部以 FIFO(先進先出)的順序?qū)υ剡M行存儲实辑。隊列中的頭元素在所有元素之中是放入時間最久的那個,而尾元素則是最短的那個藻丢。
ArrayDeque####
數(shù)組隊列 ArrayDeque的特點
- 大小自增長的隊列
- 內(nèi)部使用數(shù)組存儲數(shù)據(jù)
- 線程不安全
- 內(nèi)部數(shù)組長度為8剪撬、16、32….. 2的n次方
- 頭指針head從內(nèi)部數(shù)組的末尾開始悠反,尾指針tail從0開始残黑,在頭部插入數(shù)據(jù)時,head減一斋否,在尾部插入數(shù)據(jù)時萍摊,tail加一。當head==tail時說明數(shù)組的容量滿足不了當前的情況如叼,此時需要擴大容量為原來的二倍冰木。
執(zhí)行器服務(wù) ExecutorService####
java.util.concurrent.ExecutorService 接口表示一個異步執(zhí)行機制,使我們能夠在后臺執(zhí)行任務(wù)笼恰。因此一個 ExecutorService 很類似于一個線程池踊沸。
ExecutorService 簡單實現(xiàn)
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
首先使用 newFixedThreadPool() 工廠方法創(chuàng)建一個 ExecutorService。這里創(chuàng)建了一個十個線程執(zhí)行任務(wù)的線程池社证。
然后逼龟,將一個 Runnable 接口的匿名實現(xiàn)類傳遞給 execute() 方法。這將導(dǎo)致 ExecutorService 中的某個線程執(zhí)行該 Runnable追葡。
線程池執(zhí)行者 ThreadPoolExecutor####
java.util.concurrent.ThreadPoolExecutor 是 ExecutorService 接口的一個實現(xiàn)腺律。ThreadPoolExecutor 使用其內(nèi)部池中的線程執(zhí)行給定任務(wù)(Callable 或者 Runnable)。
構(gòu)造方法:
//構(gòu)造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面看看幾個參數(shù)的含義及作用
- corePoolSize — 核心線程數(shù)宜肉,即允許閑置的線程數(shù)目
- maximumPoolSize — 最大線程數(shù)匀钧,即這個線程池的容量
- keepAliveTime — 非核心線程的閑置存活時間
- unit — 上一個參數(shù)的單位
- workQueue — 任務(wù)隊列(阻塞隊列)
- threadFacotry — 線程創(chuàng)建工廠
- handler — 當線程池或者任務(wù)隊列容量已滿時用于 reject
Callable&&Future####
提到Callable 可能覺得比較陌生,但是Runnable大家應(yīng)該很熟悉谬返;這么說吧之斯,Callable就是帶返回值的Runnable。Callable聲明如下:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
返回值就是Callable 傳入的泛型參數(shù)的類型遣铝。而這個返回值就由Future獲取佑刷。
FutureTask####
FutureTask則是一個RunnableFuture<V>,而RunnableFuture實現(xiàn)了Runnbale又實現(xiàn)了Futrue<V>這兩個接口
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
另外它還可以包裝Runnable和Callable<V>酿炸, 由構(gòu)造函數(shù)注入依賴瘫絮。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
下面用一個簡單的demo,介紹一下Callable與FutureTask的使用
/**
* 定義一個Callable 任務(wù)填硕,返回類型為Integer
*/
public class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int hours=5;
int amount = 0;
while(hours>0){
System.out.println("I'm working,rest is "+hours);
amount++;
hours--;
Thread.sleep(1000);
}
return amount;
}
}
public class FutureTaskTest {
public static void main(String args[]) throws ExecutionException {
CallableTask worker = new CallableTask();
FutureTask<Integer> mTasks = new FutureTask<>(worker);
new Thread(mTasks).start();
while (!mTasks.isDone()) {
try {
System.out.println("job has't finsished ..." + mTasks.get());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int amount;
try {
amount = mTasks.get();
System.out.println("Job finished commited " + amount);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我們看一下輸出日志:

main 方法通過一個新的線程開始執(zhí)行mTasks 麦萤,而mTasks 又會等待worker 的執(zhí)行完畢,得到其最終執(zhí)行的結(jié)果。
好了關(guān)于基礎(chǔ)知識的了解到這里就差不多了频鉴。
AsyncTask 屬性##
有了一些基礎(chǔ)知識栓辜,首先讓我們看一下AsyncTask類的聲明:
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
//獲取當前的cpu核數(shù)
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
//ThreadFactory 線程工廠,通過工廠方法newThread來獲取新線程
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//創(chuàng)建一個鏈式阻塞隊列垛孔,默認大小128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* 假設(shè)當前所使用手機CPU為4核藕甩,那么將創(chuàng)建一個
* 線程池核心容量為5
* 線程池最大容量為9
* 非核心線程空閑時間為1秒
* 任務(wù)隊列大小為128 ---------- 的線程池(執(zhí)行器),執(zhí)行并發(fā)任務(wù)
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
* 串行任務(wù)執(zhí)行器聲明
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//消息類型:發(fā)送結(jié)果
private static final int MESSAGE_POST_RESULT = 0x1;
//消息類型: 發(fā)送過程
private static final int MESSAGE_POST_PROGRESS = 0x2;
// 設(shè)置默認執(zhí)行器為串行執(zhí)行
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// 內(nèi)部Handler實現(xiàn)
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
//獲取mWorker 執(zhí)行結(jié)果周荐;結(jié)束mWorker等功能
private final FutureTask<Result> mFuture;
//任務(wù)默認狀態(tài)為掛起
private volatile Status mStatus = Status.PENDING;
//使用原子Boolean型變量狭莱,標記當前任務(wù)是否已被取消
private final AtomicBoolean mCancelled = new AtomicBoolean();
//標記當前任務(wù)是否已被執(zhí)行過
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
/**
* Indicates the current status of the task. Each status will be set only once
* during the lifetime of a task.
*/
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
}
部分屬性已通過注釋標記,應(yīng)該很容易理解概作;下面主要看一下以下幾個聲明的具體含義:
SerialExecutor 實現(xiàn)###
SerialExecutor 串行執(zhí)行的線程池腋妙,在其內(nèi)部首先創(chuàng)建一個隊列用于存儲AsyncTask任務(wù);其中execute方法的作用就是將需要執(zhí)行的AsyncTask任務(wù)添加到這個隊列尾部讯榕,并立即執(zhí)行骤素,執(zhí)行完畢后調(diào)用scheduleNext 從隊列頭部獲取下一個任務(wù),如果這個任務(wù)不為空愚屁;就將任務(wù)交給THREAD_POOL_EXECUTOR去處理济竹;這樣如果同時有多個AsyncTask執(zhí)行時,多個添加到mTasks的任務(wù)將通過輪詢的方式逐一執(zhí)行完畢霎槐,完成串行執(zhí)行的功能送浊。
WorkerRunnable###
這個WorkRunnable又是什么呢?看看他的聲明:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
這個類繼承了Callable丘跌,并添加了一個參數(shù)袭景;我們知道Callable的參數(shù)類型是其call方法的返回類型,那么這個Params參數(shù)又有什么意義呢闭树,這個我們后面再看耸棒。
最后使用枚舉定義了任務(wù)的幾個狀態(tài);PENDING(掛起)蔼啦,RUNNING (正在運行)榆纽,F(xiàn)INISHED(已完成)。之前的代碼也可以看到捏肢,默認狀態(tài)為PENDING。
好了饥侵,至此已經(jīng)完成了AsyncTask 類當中鸵赫,大部分屬性的介紹,下面就來看看他的實現(xiàn)原理躏升。
AsyncTask 實現(xiàn)原理##
回到我們一開始提到的那個示例代碼辩棒,當我們定義了好自己的AsyncTask之后,要開始運行這個任務(wù)時非常簡單,只需要一行代碼:
new DownloadFilesTask().execute(url1, url2, url3);
我們就從這行代碼出發(fā)一睁,看看發(fā)生了什么钻弄。
構(gòu)造方法###
首先,new DownloadFileTask() 者吁,執(zhí)行DownloadFileTask的構(gòu)造方法窘俺,因此必然會執(zhí)行DownloadFileTask的父類AsyncTask的構(gòu)造方法,也就是 AsyncTask() :
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//設(shè)置當前任務(wù)已被執(zhí)行
mTaskInvoked.set(true);
//設(shè)置線程執(zhí)行的優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
構(gòu)造方法的工作很簡單复凳,就是完成了mWorker 和 mFuture 的初始化工作瘤泪,也就是Callable和Future 的初始化,并關(guān)聯(lián)他們育八,讓mFuture 可以獲取mWorker 的執(zhí)行結(jié)果对途,或者停止mWorker 的執(zhí)行。
這里主要由兩個方法call()和done()髓棋,總的來說當mFuture 開始被執(zhí)行的時候实檀,call() 就會執(zhí)行,當這個任務(wù)執(zhí)行完畢后done()方法就會執(zhí)行按声。
那么這個mFuture 什么 時候會被執(zhí)行呢劲妙?繼續(xù)往下看
任務(wù)開始真正執(zhí)行###
new DownloadFilesTask().execute(url1, url2, url3);
繼續(xù)看,上面已經(jīng)完成了構(gòu)造方法的執(zhí)行儒喊,接下來看execute() 方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
executeOnExecutor
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
到這里就很清楚了镣奋,mStatus 默認狀態(tài)為PENDING,因此任務(wù)開始執(zhí)行后首先將其狀態(tài)改為RUNNING怀愧;同時從異常判斷我們也可以看出一個AsyncTask的execute方法不能同時執(zhí)行兩次侨颈。
接下來,onPreExecute()芯义,我們是在onCreate 中開啟了AsyncTask的任務(wù)哈垢,因此這個時候,依舊屬于主線程扛拨,onPreExecute()方法也會工作在主線程耘分,我們可以在這個方法中執(zhí)行一些預(yù)備操作,初始相關(guān)內(nèi)容绑警。
mWorker求泰,前面已經(jīng)說過他就是實現(xiàn)了Callable接口,并添加了一個參數(shù)屬性计盒,在這里我們把executor中傳入的參數(shù)賦給了這個屬性渴频。exec=sDefaultExecutor=SerialExecutor ,這里任務(wù)就開始真正的執(zhí)行了北启;按照之前所說就會開始執(zhí)行mFuture這個任務(wù)卜朗,因此就會開始執(zhí)行mWorker的call方法拔第。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//設(shè)置當前任務(wù)已被執(zhí)行
mTaskInvoked.set(true);
//設(shè)置線程執(zhí)行的優(yōu)先級,被設(shè)置在后臺進行
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
到這里场钉,我們終于看到了熟悉的doInBackground蚊俺,這是我們必須實現(xiàn)的一個方法,在其中完成耗時操作逛万,并返回結(jié)果泳猬。由于已經(jīng)設(shè)置了Process的優(yōu)先級,因此這個方法會處于后臺進程泣港。
在doInBackground 里暂殖,我們還可以返回當前執(zhí)行進度:
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
我們調(diào)用了publishProgress 可以將doInBackground中耗時任務(wù)的進度發(fā)送出去,大家都知道這個進度會發(fā)送到onProgressUpdate() 方法中当纱,在onProgressUpdate我們可以方便的進行UI 更新呛每,比如進度條進度更新等。那么他是怎么實現(xiàn)的呢坡氯?這就要看publishProgress這個方法的實現(xiàn)了晨横。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
我們再來分解一下這個方法的實現(xiàn):
//返回一個InternalHandler對象
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
AsyncTaskResult#####
AsyncTaskResult 顧名思義,很好理解了箫柳,就是AsyncTask的執(zhí)行結(jié)果手形,這是一個靜態(tài)的內(nèi)部類,包括兩個屬性mTask和mData 悯恍。
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
因此publishProgress中 new AsyncTaskResult 就是創(chuàng)建了一個AsyncTaskResult,他的兩個兩個屬性為當前的AsyncTask和任務(wù)任務(wù)執(zhí)行進度库糠。
到這里的邏輯很清楚了,如果當前任務(wù)沒有被取消涮毫, 那么就從消息池中獲取一個Message的實例瞬欧,同時設(shè)置這個Message對象的msg.what=MESSAGE_POST_PROGRESS,msg.obj為一個AsyncTaskResult對象,最后執(zhí)行sendToTarget方法罢防,通過之前對Handler實現(xiàn)機制的了解艘虎,我們知道sendXXX方法殊途同歸,所完成的任務(wù)都是將Message對象插入到MessageQueue當中咒吐,等著Looper的loop方法一個個取出野建。由于我們是在主線程開啟了AsyncTask任務(wù)的執(zhí)行,因此恬叹,一旦我們將一個消息插入到隊列候生,那么就會執(zhí)行Handler的handleMessage方法。下面就來看看你這個InternalHandler 的實現(xiàn)妄呕。
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
哈哈陶舞,很簡單;在handleMessage中首先取出結(jié)果绪励,并強制轉(zhuǎn)換為AsyncTaskResult對象肿孵,在msg.what=MESSAGE_POST_PROGRESS時,就會執(zhí)行result.mTask.onProgressUpdate(result.mData);
mTask 就是當前AsyncTask疏魏,因此就會執(zhí)行AsyncTask中聲明的onProgressUpdate方法停做。這樣,就把參數(shù)從一個子線程傳遞到了UI 線程大莫,非常方便開發(fā)人員用這個完成相關(guān)業(yè)務(wù)蛉腌。
我們再回到mWorker 的call() 方法中,當doInBackground執(zhí)行完畢后只厘,最后就會執(zhí)行postResult烙丛。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
這個方法和publishProgress邏輯一樣,懂事把result 封裝到一個AsyncTaskResult 對象中羔味,做為一個Message對象的obj屬性插入到MessageQueue中河咽,只不過msg.what=MESSAGE_POST_RESULT.
這樣就會來到InternalHandler 的handleMessage中,這一次msg.what=MESSAGE_POST_RESULT.時執(zhí)行result.mTask.finish(result.mData[0]);
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
這個方法也很簡單赋元,任務(wù)未取消時忘蟹,onPostExecute(result) 方法被執(zhí)行。這個onPostExecute(result)就是我們最后要執(zhí)行的方法搁凸,在這個方法中得到最終的執(zhí)行結(jié)果媚值;并將任務(wù)狀態(tài)標記為FINISHED。
到這里护糖,終于完成了對AsyncTask常規(guī)用法的總結(jié)褥芒。先緩口氣。嫡良。锰扶。。皆刺。少辣。,如果再來一遍你會發(fā)現(xiàn)羡蛾,有個東西好像被遺忘了漓帅,什么呢?就是mFuture痴怨,我們在mWorker的call 執(zhí)行到最后時忙干,通過postResult方法將結(jié)果返回到了Handler中,依然實現(xiàn)了完成了任務(wù)浪藻,那么這個mFuture 還有什么用呢捐迫?我們再看一篇AsyncTask的構(gòu)造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//設(shè)置當前任務(wù)已被執(zhí)行
mTaskInvoked.set(true);
.....省略。爱葵。施戴。反浓。
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
。赞哗。雷则。。
省略肪笋。月劈。。藤乙。
}
};
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
我們看一下這段代碼猜揪,就會發(fā)現(xiàn)這個postResultIfNotInvoked 內(nèi)部的if語句條件不滿足,什么都不會執(zhí)行坛梁,也就意味著mFuture的done 這個方法也什么都不會做而姐。這段代碼的確十分詭異,不知道是何用意罚勾∫闳耍或者說是我沒理解其中的奧義。╮(╯_╰)╭尖殃。
這么說來丈莺,搞了半天這個mFuture貌似沒什么卵用,其實也不是送丰〉薅恚回到我們一開始提出的一個問題,如何去終止一個正在執(zhí)行的耗時任務(wù)器躏,這個問題說白了就是如何去停止一個線程俐载。通過一個外部方法讓一個正在運行的線程停止其實應(yīng)該是一件很講究的事情,這個時候mFuture 就可以大顯身手了登失。
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
AsyncTask cancel的實現(xiàn)遏佣,是靠mFuture 完成的。
這樣揽浙,平時使用AsyncTask時状婶,常用的幾個方法是怎樣實現(xiàn)的我們都了解了。
玩一玩AsyncTask
通過上面的內(nèi)容馅巷,我們已經(jīng)了解了AsyncTask的實現(xiàn)機制膛虫。下面通過一個demo來感受一下AsyncTask的奧秘。
class MyAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress();
return params[0];
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.CHINA);
Log.e(TAG, s + "execute finish at " + df.format(new Date()));
}
}
這里定義一個AsyncTask任務(wù)钓猬,實現(xiàn)邏輯很簡單稍刀,間隔一秒返回執(zhí)行任務(wù)時傳進來的參數(shù),在onPostExecute中打印方法執(zhí)行時間和執(zhí)行結(jié)果敞曹。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_at);
result = (TextView) findViewById(R.id.result);
findViewById(R.id.execute).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClick: " + Runtime.getRuntime().availableProcessors());
for (int i = 0; i < 127; i++) {
new MyAsyncTask().execute("Task#" + i);
}
}
});
}
我們通過一個for 循環(huán)會執(zhí)行127個MyAsyncTask任務(wù)账月,看一下日志:
很明顯综膀,一秒一個,默認就是串行執(zhí)行捶障。前面提到了ArrayDeque 是一個自增長的隊列僧须。因此纲刀,默認情況下项炼,可以創(chuàng)建無數(shù)個AsyncTask任務(wù)。
這127個任務(wù)串行執(zhí)行下去示绊,要等到他們都執(zhí)行完畢锭部,得2分多鐘,不行我不想等了面褐,我想要并行拌禾,那也不難,雖然
AsyncTask默認的執(zhí)行器是串行執(zhí)行展哭,但是我們可以這樣做:
for (int i = 0; i < 127; i++) {
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "AsyncTask#" + i);
}
我們可以去修改execute 的實現(xiàn)方法湃窍,替換默認的sDefaultExecutor為AsyncTask.THREAD_POOL_EXECUTOR,再次執(zhí)行我們看一下日志:
可以看到每一秒有5個線程同時執(zhí)行匪傍,這樣127個線程用26秒就可以執(zhí)行完了您市,比串行執(zhí)行快了差不多5倍。為什么是5倍呢役衡?這就要回到我們之前AsyncTask 屬性定義當中了茵休。
在定義并行執(zhí)行的線程池當時, CORE_POOL_SIZE = CPU_COUNT + 1; 線程池核心程數(shù)為CPU 核心數(shù)+1手蝎,因此在我當前的4核手機上最大線程數(shù)為5榕莺。
串行執(zhí)行的時候由于ArrayDeque 的優(yōu)點,可以依次執(zhí)行很多個任務(wù)棵介,那并行呢钉鸯?這里先說一下結(jié)論,以CPU 核心數(shù)為4的手機為例邮辽,最多一次可以執(zhí)行137個任務(wù)唠雕。這個也很好理解,
線程池最大容量為9逆巍,阻塞隊列容量為128及塘,也就說在并行的時候,最多允許137個任務(wù)被阻塞锐极,再多就不行了笙僚。
修改之前的代碼:
for (int i = 0; i < 138; i++) {
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "AsyncTask#" + i);
}
運行程序時,會拋出異常:
java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@1429681 rejected from java.util.concurrent.ThreadPoolExecutor@bfb9326[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0]
public interface RejectedExecutionHandler {
/**
* Method that may be invoked by a {@link ThreadPoolExecutor} when
* {@link ThreadPoolExecutor#execute execute} cannot accept a
* task. This may occur when no more threads or queue slots are
* available because their bounds would be exceeded, or upon
* shutdown of the Executor.
*
* <p>In the absence of other alternatives, the method may throw
* an unchecked {@link RejectedExecutionException}, which will be
* propagated to the caller of {@code execute}.
*
* @param r the runnable task requested to be executed
* @param executor the executor attempting to execute this task
* @throws RejectedExecutionException if there is no remedy
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
很明顯灵再,這次是因為我們要執(zhí)行的任務(wù)數(shù)已經(jīng)超過了線程池能夠承載的最多量肋层,因此拋出了異常亿笤。
最后##
AsyncTask 的實現(xiàn)說起來很簡單,就是封裝了Handler+Thread栋猖,但是其細節(jié)部分的實現(xiàn)净薛,有許多地方值得去深究。寫代碼的思路值得借鑒蒲拉。
AsyncTask 這個類的代碼在不同版本的SDK 有著些許差異肃拜,以上分析是基于 Android 6.0 ,也就是Android SDK 23 得出雌团。