如果在一個(gè)進(jìn)程中,頻繁創(chuàng)建和銷毀線程,顯然不是高效的做法。正確的做法是采用線程池,一個(gè)線程池中會(huì)緩存一定數(shù)量的線程,通過線程池可以避免因?yàn)轭l繁創(chuàng)建和銷毀線程所帶來的消耗。
1.AsyncTask簡(jiǎn)介
Asynctask是一個(gè)抽象類,它是Android封裝的一個(gè)輕量級(jí)異步類(輕量級(jí)體現(xiàn)在使用方便,代碼簡(jiǎn)潔),它可以在線程池中執(zhí)行后臺(tái)任務(wù),然后把執(zhí)行的進(jìn)度和最終的結(jié)果呈現(xiàn)給主線程,并且更新UI。
Asynctask內(nèi)部封裝了兩個(gè)線程池(SerialExecutor和THREAD_POOL_EXECUTOR),和一個(gè)Handler(IntentHandler)。
- SerialExecutor線程池用于任務(wù)的排隊(duì),讓需要執(zhí)行的多個(gè)耗時(shí)任務(wù)按順序排列。
- THREAD_POOL_EXECUTOR線程池才真正執(zhí)行任務(wù)。
- IntentHandler用于從工作線程切換到主線程
1.1 AsyncTask的泛型參數(shù)
AsyncTask的類型聲明:
public abstract class AsyncTask<Params, Progress, Result> {}
AsyncTask是一個(gè)抽象的泛型類。
- Params: 開始異步任務(wù)執(zhí)行時(shí)傳入的參數(shù)類型。
- Progress:異步任務(wù)執(zhí)行過程中,返回下載進(jìn)度值的類型。
-
Result: 異步任務(wù)執(zhí)行完成,返回的結(jié)果類型。
如果AsyncTask確定不需要傳遞具體參數(shù),那么這三個(gè)泛型參數(shù)可以用Void來代替。
1.2 AsyncTask的核心方法
- onPreExecute()(主線程中執(zhí)行):這個(gè)方法會(huì)在后臺(tái)任務(wù)開始執(zhí)行前調(diào)用,在主線程執(zhí)行。用于進(jìn)行一些界面上的初始化操作,比如顯示一個(gè)對(duì)話框列表什么的。
-
doInBackground(Params...)(子線程中執(zhí)行): 這個(gè)方法在子線程中執(zhí)行,負(fù)責(zé)處理耗時(shí)操作。
任務(wù)一旦完成就可以通過return語(yǔ)句來將任務(wù)的執(zhí)行結(jié)果進(jìn)行返回,如果AsyncTask的第三個(gè)參數(shù)指定的是Void,就可以不返回執(zhí)行結(jié)果。如果要更新UI,可以通過調(diào)用publishProgress(Progess...)方法來完成比如反饋當(dāng)前的進(jìn)度。 - onProgressUpdate(Progress...)(主線程中執(zhí)行):當(dāng)在后臺(tái)進(jìn)程中調(diào)用publishProgress(Progress...)方法后,這個(gè)方法就會(huì)被調(diào)用,方法中攜帶的參數(shù)就是后臺(tái)任務(wù)中傳遞過來的。在這個(gè)方法中可以對(duì)UI進(jìn)行更新操作。
- onPostExecute(Result)(主線程中執(zhí)行):當(dāng)doInBackground(Params...)執(zhí)行完畢并通過return語(yǔ)句進(jìn)行返回時(shí),這個(gè)方法就很快會(huì)被調(diào)用。返回的數(shù)據(jù)會(huì)作為參數(shù)傳遞到這個(gè)方法里面。利用參數(shù)的數(shù)據(jù),就可以進(jìn)行UI的修改。
上面幾個(gè)方法的調(diào)用順序:onPreExcute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExcute()
如果不需要執(zhí)行更新UI操作,順序是:onPreExcute() --> doInBackground() --> onPostExcute()。
- onCancelled()(主線程中執(zhí)行):該方法被調(diào)用,onPostExecute()方法將不會(huì)被執(zhí)行,需要注意的是,AsncTask中的cancel()不是真正的取消任務(wù),只是將任務(wù)標(biāo)記為取消狀態(tài),我們需要在doInBackground()內(nèi)判斷終止任務(wù)。就好比想要終止一個(gè)線程,調(diào)用interrupt()方法,只是進(jìn)行標(biāo)記為中斷,需要在線程內(nèi)部進(jìn)行標(biāo)記判斷,然后中斷線程。
1.3 AsyncTask的簡(jiǎn)單使用
public class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... voids) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("當(dāng)前下載進(jìn)度: " + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();
}
}
}
這里模擬了一個(gè)下載任務(wù),在doInBackground方法中,去執(zhí)行具體的下載邏輯,在onProgressUpdate()方法中顯示下載的進(jìn)度。在onPostExecute()方法中提示下載任務(wù)的結(jié)果。
調(diào)用方法:
new DownloadTask().execute();
1.4 使用AsyncTask的注意事項(xiàng)
- 異步任務(wù)的實(shí)例必須在UI線程中創(chuàng)建,即AsynacTask對(duì)象必須在UI線程中創(chuàng)建。
- execute(Params... params)必須在UI線程中調(diào)用。
- 不要手動(dòng)調(diào)用onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()這幾個(gè)方法。
- 不能在doInBackground()中更改UI組件的信息。
- 一個(gè)任務(wù)實(shí)例只能執(zhí)行一次,如果執(zhí)行第二次會(huì)拋出異常。
2.AsyncTask的源碼分析
AsyncTask的構(gòu)造函數(shù):
public AsyncTask() { this((Looper) null); }
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);
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);
}
}
};
}
在構(gòu)造函數(shù)中,主要是初始化了3個(gè)成員變量, mHandler,mWorker,mFuture。并在初始化mFutrue的時(shí)候,mWorker作為參數(shù)傳入。 mWorker是一個(gè)Callable對(duì)象,mFuture是以一個(gè)FutureTask對(duì)象。FutureTask實(shí)現(xiàn)了Runnable接口。
mWorker中的call()方法執(zhí)行了耗時(shí)操作,即result = doInBackground(mParams);,然后把得到的結(jié)果通過postResult(result);傳遞給內(nèi)部的Handler跳轉(zhuǎn)到主線程中。這里實(shí)際是在實(shí)例化變量,并沒有開啟執(zhí)行任務(wù)。
postResult()方法源碼:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
mFuture對(duì)象被加載線程池中,并加載的過程。
new DownloadTask().execute();其中execute()就是執(zhí)行了這一操作。
execute()源碼:
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) {
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
可以看出,execute()方法,實(shí)際上是執(zhí)行了executeOnExecutor()方法,先執(zhí)行了onPreExecute(),然后具體執(zhí)行耗時(shí)任務(wù)是 exec.execute(mFuture);。對(duì)象exec是sDefaultExecutor,往上追溯就是SerialExecutor的實(shí)例化對(duì)象。
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 是個(gè)內(nèi)部靜態(tài)內(nèi)部類,是所有實(shí)例化的AsyncTask對(duì)象共有的SerialExecutor,內(nèi)部維護(hù)了一個(gè)隊(duì)列,通過鎖使得保證AsyncTask中的任務(wù)是串行執(zhí)行的,即多個(gè)任務(wù)需要一個(gè)個(gè)加到該隊(duì)列中。
這個(gè)方法中主要有兩個(gè)步驟。
- 向隊(duì)列中加入一個(gè)新的任務(wù),即之前實(shí)例化的mFuture對(duì)象。
- 2.調(diào)用
scheduleNext(),調(diào)用THREAD_POOL_EXECUTOR執(zhí)行隊(duì)列頭部任務(wù)。
由此可以知道SerialExecutor僅僅是為了保持任務(wù)執(zhí)行隊(duì)列是串行的實(shí)際執(zhí)行交給了THREAD_POOL_EXECUTOR。
THREAD_POOL_EXECUTOR源碼:
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
實(shí)際上就是個(gè)線程池,開啟了一定數(shù)量的核心線程和工作線程。然后調(diào)用線程池的execute()方法,執(zhí)行具體的耗時(shí)任務(wù),即開頭構(gòu)造函數(shù)mWorker中的call()方法。先執(zhí)行doInBackground()方法,然后再執(zhí)行postResult()方法。
在postResult()方法中,向在AsyncTask構(gòu)造函數(shù)中初始化的mHandler發(fā)送了一個(gè)消息。
在構(gòu)造函數(shù)中通過調(diào)用getMainHandler(),實(shí)例化mHandler的源碼:
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
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中,
- 如果收到的消息是
MESSAGE_POST_PROGRESS, 回調(diào)onProgressUpdate()方法, 更新進(jìn)度。 - 如果收到的消息是
MESSAGE_POST_RESULT,即執(zhí)行完了doInBackground()方法并傳遞結(jié)果,那就調(diào)用AsyncTask.finish()方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
如果任務(wù)已經(jīng)取消了,就回調(diào)onCancelled()方法,否則就回調(diào)onPostExecute()方法。
InternalHandler是一個(gè)靜態(tài)類, 為了能夠?qū)?zhí)行環(huán)境切換到主線程, 因此這個(gè)類必須在主線程中進(jìn)行加載。 所以變相要求AsyncTask的類必須在主線程中進(jìn)行加載。到此為止, 從任務(wù)執(zhí)行的開始到結(jié)束都從源碼分析完了。
AsyncTask的串行和并行:
默認(rèn)情況下因?yàn)橛?code>SerialExecutor類來維持保證隊(duì)列的串行。如果想要使用并行執(zhí)行任務(wù),那么可以直接跳過SerialExecutor類,使用executeOnExecutor()來執(zhí)行任務(wù)。
3. AsyncTask使用不當(dāng)?shù)暮蠊?/h3>
-
1)生命周期
AsyncTask不與任何組件綁定生命周期,所以在Activity或Fragment中創(chuàng)建執(zhí)行AsyncTask時(shí)候,最好在Activity/Fragment的onDestroy()方法中調(diào)用cancle(boolean)方法。
-
2)內(nèi)存泄漏
如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會(huì)保留一個(gè)對(duì)創(chuàng)建了AsyncTask的Activity引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺(tái)線程還在執(zhí)行,它將繼續(xù)在內(nèi)存中保存這個(gè)引用,導(dǎo)致Activity無法被回收,導(dǎo)致內(nèi)存泄漏。
-
3)結(jié)構(gòu)丟失
屏幕旋轉(zhuǎn)或者Activity在后臺(tái)被系統(tǒng)殺死等情況會(huì)導(dǎo)致Activity的重新創(chuàng)建,之前運(yùn)行的AsyncTask(非內(nèi)部靜態(tài)類)會(huì)持有之前的Activity的引用,這個(gè)引用已經(jīng)無效,這時(shí)調(diào)用onPostExecute()再去更新界面將不會(huì)再生效。
AsyncTask不與任何組件綁定生命周期,所以在Activity或Fragment中創(chuàng)建執(zhí)行AsyncTask時(shí)候,最好在Activity/Fragment的onDestroy()方法中調(diào)用cancle(boolean)方法。
如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會(huì)保留一個(gè)對(duì)創(chuàng)建了AsyncTask的Activity引用。如果Activity已經(jīng)被銷毀,AsyncTask的后臺(tái)線程還在執(zhí)行,它將繼續(xù)在內(nèi)存中保存這個(gè)引用,導(dǎo)致Activity無法被回收,導(dǎo)致內(nèi)存泄漏。
屏幕旋轉(zhuǎn)或者Activity在后臺(tái)被系統(tǒng)殺死等情況會(huì)導(dǎo)致Activity的重新創(chuàng)建,之前運(yùn)行的AsyncTask(非內(nèi)部靜態(tài)類)會(huì)持有之前的Activity的引用,這個(gè)引用已經(jīng)無效,這時(shí)調(diào)用onPostExecute()再去更新界面將不會(huì)再生效。