在以前的文章深刻探究了Handler,《從Handler.post(Runnable r)再一次梳理Android的消息機制(以及handler的內存泄露)》咱們知道了Android的消息機制主要靠Handler來實現,可是在Handler的使用中,忽略內存泄露的問題,無論是代碼量仍是理解程度上都顯得有點不盡人意,因此Google官方幫咱們在Handler的基礎上封裝出了AsyncTask。可是在使用AsyncTask的時候有不少細節須要注意,它的優勢到底體如今哪裏?仍是來看看源碼一探究竟。java
來一段日常簡單使用AsyncTask來異步操做UI線程的狀況,首先新建一個類繼承AsyncTask,構造函數傳入咱們要操做的組件(ProgressBar和TextView)android
class MAsyncTask extends AsyncTask<Void, Integer, String>{
private ProgressBar mProgressBar;
private TextView mTextView;
public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
this.mProgressBar = mProgressBar;
this.mTextView = mTextView;
}
@Override
protected void onPreExecute() {
mTextView.setText("開始執行");
super.onPreExecute();
}
@Override
protected String doInBackground(Void... params) {
for(int i = 0; i <= 100; i++){
publishProgress(i);//此行代碼對應下面onProgressUpdate方法
try {
Thread.sleep(100);//耗時操做,如網絡請求
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "執行完畢";
}
@Override
protected void onProgressUpdate(Integer... values) {
mProgressBar.setProgress(values[0]);
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String s) {
mTextView.setText(s);
super.onPostExecute(s);
}
}
複製代碼
在Activity中建立咱們新建的MAsyncTask實例而且執行(無關代碼省略):bash
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
asyncTask.execute();//開始執行
...
}
}
複製代碼
在上面的代碼,咱們開了個單一的線程來執行了一個簡單的異步更新UI的操做(哈哈,可能會以爲AsyncTask有些大材小用了哈),如今來看看AsyncTask具體是怎麼實現的,先從構造方法開始:網絡
public abstract class AsyncTask<Params, Progress, Result>
複製代碼
AsyncTask爲抽象類,而且有三個泛型,我以爲這三個泛型是不少使用者不懂的根源:多線程
看了這三個泛型,咱們就基本上了解了AsyncTask的執行過程,主要就是上面代碼重寫的那幾個方法,如今來仔細看,首先在繼承AsyncTask時有個抽象方法必須重寫:併發
@WorkerThread
protected abstract Result doInBackground(Params... params);
複製代碼
顧名思義,這個方法是在後臺執行,也就是在子線程中執行,須要子類來實現,在這個方法裏面咱們能夠調用publishProgress來發送進度給UI線程,而且在onProgressUpdate方法中接收。異步
根據調用順序,咱們通常會重寫這幾個方法:async
//在doInBackground以前調用,在UI線程內執行
@MainThread
protected void onPreExecute() {
}
//在執行中,且在調用publishProgress方法時,在UI線程內執行,用於更新進度
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在doInBackground以後調用,在UI線程內執行
@MainThread
protected void onPostExecute(Result result) {
}
複製代碼
咱們來看看這個publishProgress方法是怎麼來調用onProgressUpdate方法的:ide
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
複製代碼
使用obtainMessage是避免重複建立消息,調用了getHandler()而後發送消息,這裏是一個單例函數
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
複製代碼
返回了一個InternalHandler:
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;
}
}
}
複製代碼
在判斷消息爲MESSAGE_POST_PROGRESS後咱們發現其實內部就是調用了Handler來實現這一切,包括執行結束時調用finish方法,這個咱們後面再說。從頭來看一下AsyncTask的執行過程,來到execute方法:
/**
This method must be invoked on the UI thread.(這行註釋爲Google官方註釋)
*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
複製代碼
注意!此方法必須在UI線程調用,這裏就不作測試了。在這裏又調用executeOnExecutor:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
......
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
複製代碼
咱們發如今UI線程先調用了onPreExecute(),將傳入的參數賦值給mWorker.mParams,而後調用了參數exec的execute方法,而且將mFuture做爲參數傳入,這裏就設計到了三個對象:sDefaultExecutor(在executeOnExecutor中傳入)、mWorker、mFuture,來看看它們的賦值在哪裏:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
......
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
......
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);
}
}
}
複製代碼
咱們發現sDefaultExecutor的賦值默認就是SERIAL_EXECUTOR,也就是一個順序執行的線程池,內部實現有一個任務隊列。
private final WorkerRunnable<Params, Result> mWorker;
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
......
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
複製代碼
在AsyncTask的構造方法中,給mWorker賦值爲一個Callable(帶返回參數的線程,涉及到java併發的一些基礎知識,這裏不贅述),而且在call方法中執行了doInBackground方法,最後調用postResult方法
private final FutureTask<Result> mFuture;
public AsyncTask() {
......
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);
}
}
};
}
複製代碼
mFuture爲FutureTask類型,這裏將mWorker傳入,在mWorker執行完畢後調用postResultIfNotInvoked方法,咱們先看看這個方法:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
複製代碼
其實這個方法也最後調用了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;
}
複製代碼
又看到了熟悉的obtainMessage和sendToTarget發送消息,此次消息內容變爲MESSAGE_POST_RESULT,再來看看咱們剛纔已經提到的InternalHandler的handleMessage方法:
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;
}
}
複製代碼
最後根據消息類型,這裏調用了result.mTask.finish,result類型爲AsyncTaskResult:
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
複製代碼
mTask的類型爲AsyncTask,找到AsyncTask的finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製代碼
最後若是沒有取消的話調用了onPostExecute,也就是咱們以前重寫的那個方法,在執行完畢後調用,而且此方法也在子線程。
正如開題所說,AsyncTask本質上就是對Handler的封裝,在執行以前,執行中,執行完畢都有相應的方法,使用起來也一目瞭然,不過這還並非AsyncTask的最大的優勢,AsyncTask最適合使用的場景是多線程,開始在代碼中已經看到了在AsyncTask內部有本身維護的線程池,默認的是SERIAL_EXECUTOR
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
複製代碼
按照順序執行,一個任務執行完畢再執行下一個,還提供有一個支持併發的線程池:
//獲取CPU數目
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;
//空閒線程超時時間(單位爲S)
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());
}
};
//阻塞隊列,用來保存待執行的任務(最高128個)
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
複製代碼
聲明爲static,多個實例同用一個線程池,這個是Googl官方自帶的一個根據cpu數目來優化的線程池,使用方法以下:
for(int i = 0; i < 100; i++) {//模擬100個任務,不超過128
MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
複製代碼
在executeOnExecutor中咱們還能夠傳入本身自定義的線程池:
//跟默認同樣的按順序執行
asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor());
//無限制的Executor
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
//同時執行數目爲10的Executor
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));
複製代碼
AsyncTask使用起來的確很簡單方便,內部也是Android的消息機制,而且很快捷的實現了異步更新UI,特別是多線程時也能夠很好的表現,這個是咱們單獨使用Handler時不具有的,可是在使用過程當中注意內部方法的調用順序以及調用的時機,好比asyncTask.execute() 要在UI主線程中調用,在子線程中調用是不能夠的,還有就是在使用時根據狀況來決定到底應該用哪一種線程池。