Android多執行緒---AsyncTask原始碼分析
Android多執行緒—AsyncTask使用原始碼分析
一、前言
上篇文章對 HandlerThread 的原始碼進行了分析,相信大家對 HandlerThread 有了一定的認識。如果我們需要執行一個非同步任務,並且執行完畢之後在主執行緒中更新 UI 的話,使用 HandlerThread 就不是很划算了。
但是如果採用 Handler + Thread 的寫法來進行非同步任務的話,要寫很多程式碼,首先要在主執行緒中建立一個 Handler 物件,然後再建立一個子執行緒用來處理非同步任務,然後在任務處理完的時候還要呼叫 主執行緒中的 Handler 物件去傳送訊息,主執行緒中的 Handler 物件接收到訊息以後才能在主執行緒中更新 UI,這樣做是很麻煩的。
不過 Android 給我們提供了一個更好的 Handler + Thread 的封裝類 AsyncTask 來幫助我們更好的處理非同步任務,然後更新 UI。
接下來就來看下 AsyncTask 這個幫助類。
二、初識 AsyncTask
2.1 API 文件定義
老規矩,我們看下在 API 文件裡面對 AsyncTask 的定義:
大概的意思是說:(以下翻譯自 API 文件)
AsyncTask 能夠讓我們更加容易和合適的使用 UI 執行緒,這個類允許我們在後臺執行一些操作並且傳送結果到主執行緒,而不用寫很多 Thread 和 Handler 的程式碼。
AsyncTask 被設計為一個關於 Thread 和 Handler 的幫助類,而不是提供一個通用的執行緒框架,AsyncTask 應該被用於短時間的操作,如果你需要讓執行緒長時間的執行,那麼非常推薦使用java.util.concurrent
包提供的多樣的 API ,比如 Exexutor、ThreadPoolExecutor和 FutureTask來處理。
AsyncTask 定義在子執行緒中處理計算,然後再發送結果到 UI 執行緒,主要提供了三個常用型別的引數:Params、Progress 和 result,並且有四個執行步驟:onPreExecute、doInBackground、 onProgressUpdate 、onPostExecute(
所以我們可以知道 AsyncTask 的作用就是為了解決主執行緒不能進行非同步任務和子執行緒不能更新 UI 的。
根據該類的定義
public abstract class AsyncTask<Params, Progress, Result> {}
可以看到 AsyncTask 是一個抽象類並且有三個泛型引數,
2.2 泛型引數
三個泛型引數分別是:
- Params 是 AsyncTask 執行任務所需要的引數型別
- Progress 是在後臺執行過程中,任務執行任務進度的引數型別
- Result 是最終計算得到的結果的引數型別
需要注意的是:
如果AsyncTask確定不需要傳遞具體引數,那麼這三個泛型引數可以用Void來代替。
2.3 必須實現的抽象方法
他提供的抽象方法是
protected abstract Result doInBackground(Params... params);
這個方法前面已經說了: doInBackground 方法是執行在子執行緒中處理非同步任務的,我們使用 AsyncTask 本身就是為了在子執行緒中處理非同步任務,所以必須重寫該方法。
2.4 其他的重要的可重寫的方法
另外,還有其他的一些重要的重寫方法如下
- onPreExecute(任務執行之前呼叫該方法,執行在 UI 執行緒)
- doInBackground(任務執行的時候呼叫該方法,執行在子執行緒,主要進行耗時操作)
- onProgressUpdate (在 doInBackground 方法裡面呼叫 publishProgress 方法不斷的更新進度值,不斷的呼叫該方法,執行在 UI 執行緒)
- onPostExecute(在 publishProgress 執行完以後把返回值傳遞給這個方法,在主執行緒中執行)
2.5 一些不需要重寫的重要方法
- execute(Params… params) 用於開啟 AsyncTask 的執行
- cancel(boolean mayInterruptIfRunning) 用於標記本次任務的狀態我取消狀態,並不是真正的去終止任務,而是把 mCancelled 設定為 true,所以我們還需要在 doInBackgroud 中根據 mCancelled 的值來做判斷進而終止任務的執行,這個類似於我們要終止一個執行緒,如果呼叫 interrupt() 方法 只是標記該執行緒中斷,需要線上程內部進行標記判斷來終止執行緒。
2.6 一些使用注意事項
- AsyncTask 的例項必須在 UI 執行緒建立
- execute(Params… params) 方法必須在 UI 執行緒呼叫
- 不要手動呼叫onPreExecute()、doInBackground(Params… params)、onProgressUpdate(Progress… values)、onPostExecute(Result result)這幾個方法
- doInBackground(Params… params) 執行在子執行緒,不要寫更新 UI 的操作
- 一個 AsyncTask 例項只能執行一次,如果第二次執行會報
java.lang.IllegalStateException: Cannot execute task :the task has already been executed (a task can be executed only once)
異常。 - 執行順序為 onPreExecute() –> doInBackground() –> publishProgress() –> onProgressUpdate() –> onPostExecute()
三、AsyncTask 的使用
3.1 例項展示
新建 AsyncTaskActivity
:
public class AsyncTaskActivity extends AppCompatActivity {
private static final String TAG = "AsyncTaskActivity";
private Button mButton;
private Button mCancel;
private ProgressBar mProgressBar;
private ImageView mImageView;
private MyAsyncTask mAsyncTask;
String mUrl = "https://image.uisdc.com/wp-content/uploads/2014/07/085625KMV.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
mButton = findViewById(R.id.download);
mCancel = findViewById(R.id.cancel_download);
mProgressBar = findViewById(R.id.progress);
mImageView = findViewById(R.id.image);
mAsyncTask = new MyAsyncTask();
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAsyncTask.execute(mUrl);
}
});
mCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一個正在執行的任務,onCancelled方法將會被呼叫
mAsyncTask.cancel(true);
// mAsyncTask.onCancelled("取消了");
}
});
}
class MyAsyncTask extends AsyncTask<String, Integer, String> {
private OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.readTimeout(15000, TimeUnit.MILLISECONDS)
.writeTimeout(15000, TimeUnit.MILLISECONDS)
.build();
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG, "onPreExecute: 準備下載 執行在 UI 執行緒 ");
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d(TAG, "onPostExecute: 下載完成 執行在 UI 執行緒 ");
Bitmap bitmap = BitmapFactory.decodeFile(s);
mImageView.setImageBitmap(bitmap);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//Log.d(TAG, "onProgressUpdate: 下載進度 執行在 UI 執行緒 " + values[0]);
mProgressBar.setProgress(values[0]);
}
/**
* 後臺下載圖片,最終返回圖片的本地地址
* @param strings
* @return
*/
@Override
protected String doInBackground(String... strings) {
Log.d(TAG, "doInBackground: 後臺開始下載 執行在子執行緒 " + strings[0]);
Request request = new Request.Builder()
.url(strings[0])
.build();
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
long downloadSize = 0;
long totalSize;
try {
Response response = mOkHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
inputStream = response.body().byteStream();
totalSize = response.body().contentLength();
File file = new File(Environment.getExternalStorageDirectory(), "test1111.jpg");
fileOutputStream = new FileOutputStream(file);
byte[] buffer = new byte[512 * 1024];
int len;
while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
fileOutputStream.write(buffer, 0, len);
fileOutputStream.flush();
downloadSize += len;
float percent = (float) downloadSize / totalSize;
publishProgress((int) (percent * 100));
}
return file.getAbsolutePath();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
if (null != fileOutputStream) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onCancelled(String s) {
super.onCancelled(s);
Log.d(TAG, "onCancelled: " + s);
}
@Override
protected void onCancelled() {
super.onCancelled();
Log.d(TAG, "onCancelled: 下載取消");
}
}
}
佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:id="@+id/download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="下載" />
<Button
android:id="@+id/cancel_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下載" />
<ProgressBar
android:id="@+id/progress"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/image"
android:layout_width="320dp"
android:layout_height="320dp"
android:layout_gravity="center"
android:layout_marginTop="24dp" />
</LinearLayout>
執行效果:
可以看到 AsyncTask 的使用是很簡單的,比我們寫 Thread ,然後再寫 Handler 簡單了很多。
四、原始碼分析
上面講解了 AsyncTask 的概念、方法、使用等,下面就仔細分析下,AsyncTask 到底是如何執行。
4.1 先看看 AsyncTask 的成員變數
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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;
}
可以看到,上面定義的都是和執行緒池有關的,也就是說 AsyncTask 內部的執行緒是通過執行緒池來管理的。
public abstract class AsyncTask<Params, Progress, Result> {
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
}
在靜態程式碼塊中根據前面定義的常量初始化了執行緒池,並且賦值給本地的靜態常量 THREAD_POOL_EXECUTOR。接著往下看:
public abstract class AsyncTask<Params, Progress, Result> {
/**
* 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();
// 接收最終結果的訊息標示
private static final int MESSAGE_POST_RESULT = 0x1;
// 接收進度更新的訊息標示
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
// AsyncTask 的靜態內部類,繼承於 Handler 用於從子執行緒接收到訊息,然後切換到主執行緒
private static InternalHandler sHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
// 當前狀態
private volatile Status mStatus = Status.PENDING;
// 是否取消
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
// 主執行緒的 Handler
private final Handler mHandler;
}
這裡主要定義了一些關鍵的成員常量,來看幾個主要的
4.1.1 SERIAL_EXECUTOR
// 這裡又建立了一個執行緒池,主要是用於多個耗時任務的時候,使其按照順序排列
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
SerialExecutor 是 AsyncTask 的一個內部類:
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);
}
}
}
SerialExecutor 實現了 Executor 介面,內部維護了一個佇列 ArrayDeque mTasks,以及一個成員變數 Runnable 。
在 execute 方法中,首先通過 mTasks.offer 方法把新建一個 Runnable 物件新增到佇列尾部,然後再 run 方法裡面去執行傳遞給 execute 方法的 Runnable 物件,
if (mActive == null) {
scheduleNext();
}
如果當前的 mActive 是空的就去執行 scheduleNext() ,scheduleNext() 方法裡面其實就是通過 AyyayDeque 的 pool() 方法從任務佇列 mTasks 中刪除佇列中的第一個元素,並把返回值賦值給 mActive,如果第一個元素不為空(也就是 mTasks 中有任務還未執行,就去呼叫 THREAD_POOL_EXECUTOR(真正的執行緒執行者)去執行這個任務。)如果執行 mTasks 的 pool 方法返回的值為 null,那麼什麼都不做。
emmmmmmmmmmmmmmmmmmmmmmmmm
可能有點暈,簡單再說下:
也就是說這個 SerialExecutor 存在的意義並不是用來具體的執行任務的,,它內部維護這一個佇列,當呼叫他的 execute() 方法執行任務的時候,會先向其內部的佇列 mTasks 尾部新增一個任務 (Runnable),然後判斷其內部的 mActive 是不是有任務在執行,如果沒有任務在執行,那麼就從內部任務佇列中取出一個任務去執行,直到任務執行完畢。
也就是說,SerialExecutor 物件,它只負責維護一個佇列,讓任務能夠按照一定的順序執行,真正執行任務的是在靜態程式碼塊中建立的執行緒池 THREAD_POOL_EXECUTOR。也就是說在 THREAD_POOL_EXECUTOR 這個執行緒池中,一次只能夠執行到一個任務。
4.1.2 mCancelled 標記任務是否取消
4.1.3 mTaskInvoked 標記任務是否執行
4.1.4 mStatus 標記任務狀態
4.1.5 sHandler 任務執行緒的 Handler
4.1.6 mHandler 主執行緒的 Handler
4.2 從入口看起 execute(Params… params)
我們先從執行任務的入口 execute 方法看起,原始碼如下:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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 進行判斷,只要不是 PENDING(待執行) 狀態的,都會丟擲異常,而只要一個 AsyncTask 物件執行了 execute 方法,就會執行 mStatus = Status.RUNNING;
那麼其狀態就會被設定成 EUNNING,這也就解釋了為什麼一個 AsyncTask 物件只能被執行一次。
onPreExecute();
這段程式碼執行在主執行緒中,驗證了前面說的 onPreExecute 方法是在非同步任務執行前呼叫的,如果重寫了 onPreExecute();就會在任務開始前進行呼叫。
mWorker.mParams = params;
這行程式碼是把我們傳入的引數 賦值給 mWorker中的變數 mParams,我們先來看下 mWorker 是什麼鬼?
private final WorkerRunnable<Params, Result> mWorker;
可以看到 mWorker 是 WorkerRunnable 的物件,
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable 又是 AsyncTask 的靜態內部抽象類,並且實現了 Callable 介面,並且內部儲存了我們傳入的引數。那麼他是什麼時候賦值的呢?繼續往下看。
繼續看下面一行:
exec.execute(mFuture);
exec 就是 AsyncTask 的 execute 方法我們傳進來的執行緒池。mFuture 又是什麼鬼?
mFuture 是 FutureTask 的物件, FutureTask 實現了 RunnableFuture 介面,而 RunnableFuture 介面又繼承於 Runnable 和 Future,所有這裡的 mFuture 可以實現在子執行緒中執行任務並且得到最終執行返回值的功能,繼續往下看
我們建立 AsyncTask 物件時候,會呼叫其參構造方法:
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return 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);
}
}
};
}
可以看到我們上面提到的 mWorker 和 mFuture 都是在這裡建立的。
先來看 mWorker:
執行 mWorker 中的 call 方法的時候,程式碼已經處於子執行緒了,這時會把 mTaskInvoked 設定為 true,並且把引數傳遞給 doInBackground方法執行,所以說 doInBackground 是執行在子執行緒中的,我們可以做一些耗時任務。
最終會把 doInBackground 方法的到的返回值傳遞給 postResult 方法。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在 postResult 方法中,通過 getHandler() 把完成的訊息傳遞出去,看下: getHandler() 這個方法:
private Handler getHandler() {
return mHandler;
}
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
沒錯,這個Handler 就是通過主執行緒的 Looper 來建立的,是屬於主執行緒的 Handler。也就是說,在主執行緒的 mHandler 接收到訊息之後,以後的程式碼都是執行在主執行緒中了。
再來看下這個通過主執行緒 Handler 傳送出的 Message 內容:
- what 的值為 MESSAGE_POST_RESULT ,,這個是一個常量
obj 的值為
new AsyncTaskResult<Result>(this, result)
AsyncTaskResult :
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
AsyncTaskResult 物件裡面儲存了 AsyncTask 物件和最終的計算結果。
那麼在哪裡接收這個訊息呢?
對的,直接搜尋 MESSAGE_POST_RESULT 在哪使用的就行了,我找到了:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
就是這個 Internalhandler,它繼承了 Handler,所以有處理訊息的能力。
這裡先拿到 Message.obj 的值賦值給 AsyncTaskResult
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
這裡先判斷任務有沒有取消掉,如果取消掉了,那麼就呼叫 onCancelled(result);,不會呼叫 onPostExecute(result);反之亦然,這也說明了 onCancelled(result) 和 onPostExecute(result); 肯定不會同時呼叫的。
最後再把當前任務的狀態設定為完成 Status.FINISHED
。
與之類似的是,我們獲取當前任務的進度值也是通過該種機制來進行的,有興趣的可以去看下,這裡就不多說了。
接下來看下 mFuture:
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);
}
}
};
前面說了 mWorker 是實現了 Callable 介面的,在建立 mFuture 的構造引數中可以接收一個 Callable 型別的物件,這裡會把 mWorker 傳遞進去,然後在執行 mFuture 的 done 方法時呼叫 postResultIfNotInvoked(get());
方法。
get() 表示獲取 mWorker 的 call 的最終計算返回值,即最終的 Result。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
最終會根據 mTaskInvoked.get() 來獲取該任務是否執行,如果沒被執行,那麼就執行 postResult(result);
但是,我們前面在初始化 mWorker 的時候,會先執行了 mTaskInvoked.set(true);
,所以,這裡的 postResult(result); 正常情況下是執行不到的。
到這裡大概已經清楚 mWorker 和 mFuture 是幹嘛的了?
但是需要注意的是,上面只是初始化了 mWorker 和 mFuture,並沒有去執行 mWorker 中的 call 方法和 mFuture done 方法。
那 mWorker 中的 call 方法和 mFuture done 方法什麼時候執行呢?
呼叫 exec.execute(mFuture)
了以後,就會去呼叫 mWorker 中的 call 方法,call 方法中會去呼叫 doInBackground 方法去執行非同步任務,然後任務執行完畢以後,會呼叫 mFuture 的 done 方法,表示任務執行結束,然後就在 done 方法中呼叫 get() 方法獲取執行結果傳遞給 postResultIfNotInvoked(get())
了。
五、最後
至此,一次完整的非同步任務執行流程就分析完畢了,相信你對 AsyncTask 應該有了更加清晰的認識了。
下篇文章我們來看下 IntentService 的相關程式碼。