深入理解AsyncTask
眾所周知,在Android中如果要執行耗時的操作,一般是在子線程中處理,使用new Thread的方法實現是最常見的方法之一。今天,我們要講的是另外一個,Android提供的異步任務類AsyncTask,底層是使用線程池實現的。
一、Android的線程
線程是操作系統的最小執行單位,它的創建和銷毀都會消耗一定的系統資源,如果頻繁的創建和銷毀,顯然不是高效的做法,正確的做法是,采用線程池,緩存一定量的線程,通過復用這些線程,避免造成極大的系統開銷。
二、AsyncTask
這是一個抽象類,使用方便,代碼簡潔,所以說是一個輕量級的異步類。它可以在線程池中執行後臺任務,然後把執行的進度和結果通過Handler傳給UI線程進而刷新視圖。
該類的聲明如下:
public abstract class AsyncTask<Params, Progress, Result>
其中各個參數的含義如下:
Params:開始異步任務執行時傳入的參數類型;
Progress:異步任務執行過程中,返回下載進度值的類型;
Result:異步任務執行完成後,返回的結果類型;
如果AsyncTask確定不需要傳遞具體參數,那麽這三個泛型參數可以用Void來代替。
三、AsyncTask源碼
AsyncTask內部封裝了2個線程池:SerialExecutor和THREAD_POOL_EXECUTOR,和1個Handler(IntentHandler)。其中SerialExecutor線程池用於任務的排隊,讓需要執行的多個耗時任務,按順序排列,THREAD_POOL_EXECUTOR線程池才真正地執行任務,InternalHandler用於從工作線程切換到主線程。部分源碼如下:
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); } } }
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor;
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; } } }
四、註意事項
1、因為執行後的結果要傳遞到主線程,所以使用Handler進行工作線程和主線程的切換,所以AsyncTask創建實例和execute需要在主線程調用;
2、onPreExecute(),onProgressUpdate(Progress... values),onPostExecute(Result result),onCancelled()方法是在主線程執行的,而doInBackground(Params... params)是在工作線程執行;
3、執行順序:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute(),如果不需要執行更新進度則為onPreExecute() --> doInBackground() --> onPostExecute(),其中publishProgress方法在doInBackground方法中調用,會觸發onProgressUpdate方法;
4、AsyncTask還提供了onCancelled()方法,它同樣在主線程中執行,當異步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用,但是要註意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要在線程內部進行標記判斷然後中斷線程。
5、一個任務實例只能執行一次,如果執行第二次將會拋出異常。
五、AsyncTask使用不當的後果
1、生命周期:AsyncTask不與任何組件綁定生命周期,所以在Activity或者Fragment中創建執行AsyncTask時,最好在Activity或Fragment的onDestory()調用 cancel(boolean);
2、內存泄漏:如果AsyncTask被聲明為Activity的非靜態的內部類,那麽AsyncTask會保留一個對創建了AsyncTask的Activity的引用。如果Activity已經被銷毀,AsyncTask的後臺線程還在執行,它將繼續在內存裏保留這個引用,導致Activity無法被回收,引起內存泄露;
3、 結果丟失:屏幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。
參考鏈接:
http://www.jianshu.com/p/817a34a5f200
深入理解AsyncTask