前言
在之前的文章深入探究了Handler器仗,《從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)》我們知道了Android的消息機(jī)制主要靠Handler來實現(xiàn),但是在Handler的使用中橄妆,忽略內(nèi)存泄露的問題爱榔,不管是代碼量還是理解程度上都顯得有點不盡人意,所以Google官方幫我們在Handler的基礎(chǔ)上封裝出了AsyncTask。但是在使用AsyncTask的時候有很多細(xì)節(jié)需要注意,它的優(yōu)點到底體現(xiàn)在哪里允坚?還是來看看源碼一探究竟。
怎么使用
來一段平常簡單使用AsyncTask來異步操作UI線程的情況蛾号,首先新建一個類繼承AsyncTask,構(gòu)造函數(shù)傳入我們要操作的組件(ProgressBar和TextView)
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("開始執(zhí)行");
super.onPreExecute();
}
@Override
protected String doInBackground(Void... params) {
for(int i = 0; i <= 100; i++){
publishProgress(i);//此行代碼對應(yīng)下面onProgressUpdate方法
try {
Thread.sleep(100);//耗時操作涯雅,如網(wǎng)絡(luò)請求
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "執(zhí)行完畢";
}
@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中創(chuàng)建我們新建的MAsyncTask實例并且執(zhí)行(無關(guān)代碼省略):
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
asyncTask.execute();//開始執(zhí)行
...
}
}
看看原理
在上面的代碼鲜结,我們開了個單一的線程來執(zhí)行了一個簡單的異步更新UI的操作(哈哈,可能會覺得AsyncTask有些大材小用了哈)活逆,現(xiàn)在來看看AsyncTask具體是怎么實現(xiàn)的精刷,先從構(gòu)造方法開始:
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask為抽象類,并且有三個泛型蔗候,我覺得這三個泛型是很多使用者不懂的根源:
- params:參數(shù)怒允,在execute() 傳入,可變長參數(shù)锈遥,跟doInBackground(Void... params) 這里的params類型一致纫事,我這里沒有傳參數(shù),所以可以將這個泛型設(shè)置為Void
- Progress:執(zhí)行的進(jìn)度所灸,跟onProgressUpdate(Integer... values) 的values的類型一致丽惶,一般情況為Integer
- Result:返回值,跟String doInBackground 返回的參數(shù)類型一致爬立,且跟onPostExecute(String s) 的s參數(shù)一致钾唬,在耗時操作執(zhí)行完畢調(diào)用。我這里執(zhí)行完畢返回了個字符串,所以為String
看了這三個泛型抡秆,我們就基本上了解了AsyncTask的執(zhí)行過程奕巍,主要就是上面代碼重寫的那幾個方法,現(xiàn)在來仔細(xì)看儒士,首先在繼承AsyncTask時有個抽象方法必須重寫:
@WorkerThread
protected abstract Result doInBackground(Params... params);
顧名思義的止,這個方法是在后臺執(zhí)行,也就是在子線程中執(zhí)行乍桂,需要子類來實現(xiàn)冲杀,在這個方法里面我們可以調(diào)用publishProgress來發(fā)送進(jìn)度給UI線程,并且在onProgressUpdate方法中接收睹酌。
根據(jù)調(diào)用順序权谁,我們一般會重寫這幾個方法:
//在doInBackground之前調(diào)用,在UI線程內(nèi)執(zhí)行
@MainThread
protected void onPreExecute() {
}
//在執(zhí)行中憋沿,且在調(diào)用publishProgress方法時旺芽,在UI線程內(nèi)執(zhí)行,用于更新進(jìn)度
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在doInBackground之后調(diào)用辐啄,在UI線程內(nèi)執(zhí)行
@MainThread
protected void onPostExecute(Result result) {
}
我們來看看這個publishProgress方法是怎么來調(diào)用onProgressUpdate方法的:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
使用obtainMessage是避免重復(fù)創(chuàng)建消息采章,調(diào)用了getHandler()然后發(fā)送消息,這里是一個單例
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后我們發(fā)現(xiàn)其實內(nèi)部就是調(diào)用了Handler來實現(xiàn)這一切,包括執(zhí)行結(jié)束時調(diào)用finish方法壶辜,這個我們后面再說悯舟。從頭來看一下AsyncTask的執(zhí)行過程,來到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線程調(diào)用抵怎,這里就不做測試了。在這里又調(diào)用executeOnExecutor:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
......
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
我們發(fā)現(xiàn)在UI線程先調(diào)用了onPreExecute()岭参,將傳入的參數(shù)賦值給mWorker.mParams反惕,然后調(diào)用了參數(shù)exec的execute方法,并且將mFuture作為參數(shù)傳入演侯,這里就設(shè)計到了三個對象:sDefaultExecutor(在executeOnExecutor中傳入)姿染、mWorker、mFuture秒际,來看看它們的賦值在哪里:
sDefaultExecutor:
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);
}
}
}
我們發(fā)現(xiàn)sDefaultExecutor的賦值默認(rèn)就是SERIAL_EXECUTOR悬赏,也就是一個順序執(zhí)行的線程池,內(nèi)部實現(xiàn)有一個任務(wù)隊列娄徊。
mWorker
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的構(gòu)造方法中舷嗡,給mWorker賦值為一個Callable(帶返回參數(shù)的線程,涉及到j(luò)ava并發(fā)的一些基礎(chǔ)知識嵌莉,這里不贅述)进萄,并且在call方法中執(zhí)行了doInBackground方法,最后調(diào)用postResult方法
mFuture
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執(zhí)行完畢后調(diào)用postResultIfNotInvoked方法可婶,我們先看看這個方法:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
其實這個方法也最后調(diào)用了postResult,在這之前做了個有沒調(diào)用的判斷援雇,確保任務(wù)執(zhí)行完畢后調(diào)用此方法矛渴。來看看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發(fā)送消息,這次消息內(nèi)容變?yōu)?strong>MESSAGE_POST_RESULT惫搏,再來看看我們剛才已經(jīng)提到的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;
}
}
最后根據(jù)消息類型具温,這里調(diào)用了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;
}
最后如果沒有取消的話調(diào)用了onPostExecute铣猩,也就是我們之前重寫的那個方法,在執(zhí)行完畢后調(diào)用茴丰,并且此方法也在子線程达皿。
多線程并發(fā)
正如開題所說,AsyncTask本質(zhì)上就是對Handler的封裝贿肩,在執(zhí)行之前峦椰,執(zhí)行中,執(zhí)行完畢都有相應(yīng)的方法汰规,使用起來也一目了然汤功,不過這還并不是AsyncTask的最大的優(yōu)點,AsyncTask最適合使用的場景是多線程溜哮,開始在代碼中已經(jīng)看到了在AsyncTask內(nèi)部有自己維護(hù)的線程池冤竹,默認(rèn)的是SERIAL_EXECUTOR
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
按照順序執(zhí)行,一個任務(wù)執(zhí)行完畢再執(zhí)行下一個茬射,還提供有一個支持并發(fā)的線程池:
//獲取CPU數(shù)目
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心工作線程(同時執(zhí)行的線程數(shù))
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//線程池允許的最大線程數(shù)目
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());
}
};
//阻塞隊列,用來保存待執(zhí)行的任務(wù)(最高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官方自帶的一個根據(jù)cpu數(shù)目來優(yōu)化的線程池,使用方法如下:
for(int i = 0; i < 100; i++) {//模擬100個任務(wù)萧恕,不超過128
MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
在executeOnExecutor中我們還可以傳入自己自定義的線程池:
//跟默認(rèn)一樣的按順序執(zhí)行
asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor());
//無限制的Executor
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
//同時執(zhí)行數(shù)目為10的Executor
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));
總結(jié)
AsyncTask使用起來的確很簡單方便刚梭,內(nèi)部也是Android的消息機(jī)制,并且很快捷的實現(xiàn)了異步更新UI票唆,特別是多線程時也可以很好的表現(xiàn)朴读,這個是我們單獨使用Handler時不具備的,但是在使用過程中注意內(nèi)部方法的調(diào)用順序以及調(diào)用的時機(jī)走趋,比如asyncTask.execute() 要在UI主線程中調(diào)用衅金,在子線程中調(diào)用是不可以的,還有就是在使用時根據(jù)情況來決定到底應(yīng)該用哪種線程池。