轉載請註明出處(萬分感謝!):
http://blog.csdn.net/javazejian/article/details/52464139
出自【zejian的博客】
關聯文章:
Android 多線程之HandlerThread 完全詳解
Android 多線程之IntentService 完全詳解
android多線程-AsyncTask之工作原理深入解析(上)
android多線程-AsyncTask之工作原理深入解析(下)
上篇分析AsyncTask的一些基本用法以及不同android版本下的區別,接着本篇我們就來全面剖析一下AsyncTask的工作原理。在開始之前我們先來了解一個多線程的知識點——Callable<V> 、Future<V>和FutureTask類
一、理解Callable<V> 、Future<V>以及FutureTask類
Callable<V>
Callable的接口定義如下:
public interface Callable<V> {
V call() throws Exception;
}
Callable接口聲明瞭一個名稱爲call()的方法,該方法可以有返回值V,也可以拋出異常。Callable也是一個線程接口,它與Runnable的主要區別就是Callable在線程執行完成後可以有返回值而Runnable沒有返回值,Runnable接口聲明如下:
public interface Runnable {
public abstract void run();
}
那麼Callable接口如何使用呢,Callable需要和ExcutorService結合使用,其中ExecutorService也是一個線程池對象繼承自Executor接口,對於線程池的知識點不瞭解可以看看我的另一篇文章,這裏就不深入了,接着看看ExecutorService提供了那些方法供我們使用:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
- submit(Callable task),傳遞一個實現Callable接口的任務,並且返回封裝了異步計算結果的Future。
- submit(Runnable task, T result),傳遞一個實現Runnable接口的任務,並且指定了在調用Future的get方法時返回的result對象。
- submit(Runnable task),傳遞一個實現Runnable接口的任務,並且返回封裝了異步計算結果的Future。
因此我們只要創建好我們的線程對象(實現Callable接口或者Runnable接口),然後通過上面3個方法提交給線程池去執行即可。Callable接口介紹就先到這,再來看看Future時什麼鬼。
Future<V>
Future接口是用來獲取異步計算結果的,說白了就是對具體的Runnable或者Callable對象任務執行的結果進行獲取(get()),取消(cancel()),判斷是否完成等操作。其方法如下:
public interface Future<V> {
//取消任務
boolean cancel(boolean mayInterruptIfRunning);
//如果任務完成前被取消,則返回true。
boolean isCancelled();
//如果任務執行結束,無論是正常結束或是中途取消還是發生異常,都返回true。
boolean isDone();
//獲取異步執行的結果,如果沒有結果可用,此方法會阻塞直到異步計算完成。
V get() throws InterruptedException, ExecutionException;
// 獲取異步執行結果,如果沒有結果可用,此方法會阻塞,但是會有時間限制,
//如果阻塞時間超過設定的timeout時間,該方法將返回null。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
總得來說Future有以下3點作用:
- 能夠中斷執行中的任務
- 判斷任務是否執行完成
- 獲取任務執行完成後額結果。
但是Future只是接口,我們根本無法將其創建爲對象,於官方又給我們提供了其實現類FutureTask,這裏我們要知道前面兩個接口的介紹都只爲此類做鋪墊,畢竟AsncyTask中使用到的對象是FutureTask。
FutureTask
先來看看FutureTask的實現:
public class FutureTask<V> implements RunnableFuture<V> {
顯然FutureTask類實現了RunnableFuture接口,我們再看一下RunnableFuture接口的實現:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
從接口實現可以看出,FutureTask除了實現了Future接口外還實現了Runnable接口,因此FutureTask既可以當做Future對象也可是Runnable對象,當然FutureTask也就可以直接提交給線程池來執行。接着我們最關心的是如何創建FutureTask對象,實際上可以通過如下兩個構造方法來構建FutureTask
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
從構造方法看出,我們可以把一個實現了Callable或者Runnable的接口的對象封裝成一個FutureTask對象,然後通過線程池去執行,那麼具體如何使用呢?簡單案例,CallableDemo.java代碼如下:
package com.zejian.Executor;
import java.util.concurrent.Callable;
/**
* Callable接口實例 計算累加值大小並返回
*/
public class CallableDemo implements Callable<Integer> {
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子線程開始計算啦!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子線程計算結束!");
return sum;
}
}
CallableTest.java測試代碼如下:
package com.zejian.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) {
//第一種使用方式
// //創建線程池
// ExecutorService es = Executors.newSingleThreadExecutor();
// //創建Callable對象任務
// CallableDemo calTask=new CallableDemo();
// //提交任務並獲取執行結果
// Future<Integer> future =es.submit(calTask);
// //關閉線程池
// es.shutdown();
//第二中使用方式
//創建線程池
ExecutorService es = Executors.newSingleThreadExecutor();
//創建Callable對象任務
CallableDemo calTask=new CallableDemo();
//創建FutureTask
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//執行任務
es.submit(futureTask);
//關閉線程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主線程在執行其他任務");
if(futureTask.get()!=null){
//輸出獲取到的結果
System.out.println("futureTask.get()-->"+futureTask.get());
}else{
//輸出獲取到的結果
System.out.println("futureTask.get()未獲取到結果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主線程在執行完成");
}
}
代碼非常簡單,註釋也很明朗,這裏我們分析一下第2種執行方式,先前聲明一個CallableDemo類,該類實現了Callable接口,接着通過call方法去計算sum總值並返回。然後在測試類CallableTest中,把CallableDemo實例類封裝成FutureTask對象並交給線程池去執行,最終執行結果將封裝在FutureTask中,通過FutureTask#get()可以獲取執行結果。第一種方式則是直接把Callable實現類丟給線程池執行,其結果封裝在Future實例中,第2種方式執行結果如下:
Callable子線程開始計算啦!
主線程在執行其他任務
Callable子線程計算結束!
futureTask.get()-->12497500
主線程在執行完成
ok~,到此我們對Callable、Future和FutureTask就介紹到這,有了這個知識鋪墊,我們就可以愉快的撩開AsyncTask的內部工作原理了。
二、AsyncTask的工作原理完全解析
在上篇中,使用瞭如下代碼來執行AsyncTask的異步任務:
new AysnTaskDiff("AysnTaskDiff-1").execute("");
從代碼可知,入口是execute方法,那我們就先看看execute的源碼:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
很明顯execute方法只是一個殼子,直接調用了executeOnExecutor(sDefaultExecutor, params)
,其中sDefaultExecutor是一個串行的線程池,接着看看sDefaultExecutor內部實現:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//串行線程池類,實現Executor接口
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() { //插入一個Runnble任務
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//判斷是否有Runnable在執行,沒有就調用scheduleNext方法
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//從任務隊列mTasks中取出任務並放到THREAD_POOL_EXECUTOR線程池中執行.
//由此也可見任務是串行進行的。
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
從源碼可以看出,ArrayDeque是一個存放任務隊列的容器(mTasks),任務Runnable傳遞進來後交給SerialExecutor的execute方法處理,SerialExecutor會把任務Runnable插入到任務隊列mTasks尾部,接着會判斷是否有Runnable在執行,沒有就調用scheduleNext方法去執行下一個任務,接着交給THREAD_POOL_EXECUTOR線程池中執行,由此可見SerialExecutor並不是真正的線程執行者,它只是是保證傳遞進來的任務Runnable(實例是一個FutureTask)串行執行,而真正執行任務的是THREAD_POOL_EXECUTOR線程池,當然該邏輯也體現AsyncTask內部的任務是默認串行進行的。順便看一下THREAD_POOL_EXECUTOR線程池的聲明:
//CUP核數
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;
//非核心線程的存活時間1s
private static final int KEEP_ALIVE = 1;
//線程工廠類
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());
}
};
//線程隊列,核心線程不夠用時,任務會添加到該隊列中,隊列滿後,會去調用非核心線程執行任務
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
* 創建線程池
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
ok~,關於sDefaultExecutor,我們先瞭解到這,回到之前execute方法內部調用的executeOnExecutor方法的步驟,先來看看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()在此執行了!!!
onPreExecute();
//參數傳遞給了mWorker.mParams
mWorker.mParams = params;
//執行mFuture任務,其中exec就是傳遞進來的sDefaultExecutor
//把mFuture交給線程池去執行任務
exec.execute(mFuture);
return this;
}
從executeOnExecutor方法的源碼分析得知,執行任務前先會去判斷當前AsyncTask的狀態,如果處於RUNNING和FINISHED狀態就不可再執行,直接拋出異常,只有處於Status.PENDING時,AsyncTask纔會去執行。然後onPreExecute()被執行的,該方法可以用於線程開始前做一些準備工作。接着會把我們傳遞進來的參數賦值給 mWorker.mParams ,並執行開始執行mFuture任務,那麼mWorker和mFuture到底是什麼?先看看mWorker即WorkerRunnable的聲明源碼:
//抽象類
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable抽象類實現了Callable接口,因此WorkerRunnable本質上也算一個Callable對象,其內部還封裝了一個mParams的數組參數,因此我們在外部執行execute方法時傳遞的可變參數最終會賦值給WorkerRunnable的內部數組mParams,這些參數最後會傳遞給doInBackground方法處理,這時我們發現doInBackground方法也是在WorkerRunnable的call方法中被調用的,看看其源碼如下:
public AsyncTask() {
//創建WorkerRunnable mWorker,本質上就是一個實現了Callable接口對象
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//設置標誌
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//執行doInBackground,並傳遞mParams參數
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
//執行完成調用postResult方法更新結果
return postResult(result);
}
};
//把mWorker(即Callable實現類)封裝成FutureTask實例
//最終執行結果也就封裝在FutureTask中
mFuture = new FutureTask<Result>(mWorker) {
//任務執行完成後被調用
@Override
protected void done() {
try {
//如果還沒更新結果通知就執行postResultIfNotInvoked
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);
}
}
};
}
可以看到在初始化AsyncTask時,不僅創建了mWorker(本質實現了Callable接口的實例類)而且也創建了FutureTask對象,並把mWorker對象封裝在FutureTask對象中,最後FutureTask對象將在executeOnExecutor方法中通過線程池去執行。給出下圖協助理解:
AsynTask在初始化時會創建mWorker實例對象和FutureTask實例對象,mWorker是一個實現了Callable線程接口並封裝了傳遞參數的實例對象,然後mWorker實例會被封裝成FutureTask實例中。在AsynTask創建後,我們調用execute方法去執行異步線程,其內部又直接調用了executeOnExecutor方法,並傳遞了線程池exec對象和執行參數,該方法內部通過線程池exec對象去執行mFuture實例,這時mWorker內部的call方法將被執行並調用doInBackground方法,最終通過postResult去通知更新結果。關於postResult方法,其源碼如下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
顯然是通過Handler去執行結果更新的,在執行結果成返回後,會把result封裝到一個AsyncTaskResult對象中,最後把MESSAGE_POST_RESULT標示和AsyncTaskResult存放到Message中併發送給Handler去處理,這裏我們先看看AsyncTaskResult的源碼:
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
顯然AsyncTaskResult封裝了執行結果的數組以及AsyncTask本身,這個沒什麼好說的,接着看看AsyncTaskResult被髮送到handler後如何處理的。
private static class InternalHandler extends Handler {
public InternalHandler() {
//獲取主線程的Looper傳遞給當前Handler,這也是爲什麼AsyncTask只能在主線程創建並執行的原因
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
//獲取AsyncTaskResult
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:
//執行onProgressUpdate方法,自己實現。
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
從Handler的源碼分析可知,該handler綁定的線程爲主線線程,這也就是爲什麼AsyncTask必須在主線程創建並執行的原因了。接着通過handler發送過來的不同標誌去決定執行那種結果,如果標示爲MESSAGE_POST_RESULT則執行AsyncTask的finish方法並傳遞執行結果給該方法,finish方法源碼如下:
private void finish(Result result) {
if (isCancelled()) {//判斷任務是否被取消
onCancelled(result);
} else {//執行onPostExecute(result)並傳遞result結果
onPostExecute(result);
}
//更改AsyncTask的狀態爲已完成
mStatus = Status.FINISHED;
}
該方法先判斷任務是否被取消,如果沒有被取消則去執行onPostExecute(result)方法,外部通過onPostExecute方法去更新相關信息,如UI,消息通知等。最後更改AsyncTask的狀態爲已完成。到此AsyncTask的全部流程執行完。
這裏還有另一種標誌MESSAGE_POST_PROGRESS,該標誌是我們在doInBackground方法中調用publishProgress方法時發出的,該方法原型如下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
//發送MESSAGE_POST_PROGRESS,通知更新進度條
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
ok~,AsyncTask的整體流程基本分析完,最後來個總結吧:當我們調用execute(Params… params)方法後,其內部直接調用executeOnExecutor方法,接着onPreExecute()被調用方法,執行異步任務的WorkerRunnable對象(實質爲Callable對象)最終被封裝成FutureTask實例,FutureTask實例將由線程池sExecutor執行去執行,這個過程中doInBackground(Params… params)將被調用(在WorkerRunnable對象的call方法中被調用),如果我們覆寫的doInBackground(Params… params)方法中調用了publishProgress(Progress… values)方法,則通過InternalHandler實例sHandler發送一條MESSAGE_POST_PROGRESS消息,更新進度,sHandler處理消息時onProgressUpdate(Progress… values)方法將被調用;最後如果FutureTask任務執行成功並返回結果,則通過postResult方法發送一條MESSAGE_POST_RESULT的消息去執行AsyncTask的finish方法,在finish方法內部onPostExecute(Result result)方法被調用,在onPostExecute方法中我們可以更新UI或者釋放資源等。這既是AsyncTask內部的工作流程,可以說是Callable+FutureTask+Executor+Handler內部封裝。結尾我們獻上一張執行流程,協助大家理解整個流程:
好~,本篇到此結束。。。
Android 多線程之HandlerThread 完全詳解
Android 多線程之IntentService 完全詳解
android多線程-AsyncTask之工作原理深入解析(上)
android多線程-AsyncTask之工作原理深入解析(下)
主要參考資料:
https://developers.android.com
《android開發藝術探索》