詳解Android中用于線程處理的AsyncTask類的用法及源碼
為什么要用AsyncTask
我們寫App都有一個原則,主線程不能夠運行需要占用大量CPU時間片的任務(wù),如大量復(fù)雜的浮點運算,較大的磁盤IO操作,網(wǎng)絡(luò)socket等,這些都會導(dǎo)致我們的主線程對用戶的響應(yīng)變得遲鈍,甚至ANR,這些會使應(yīng)用的用戶體驗變差,但是有時又的確需要執(zhí)行這些耗時的任務(wù),那么我們通??梢允褂肁syncTask或者new Thread
來處理,這樣把任務(wù)放入工作線程中執(zhí)行,不會占用主線程的時間片,所以主線程會及時響應(yīng)用戶的操作,如果使用new Thread來執(zhí)行任務(wù),那么如果需要中途取消任務(wù)執(zhí)行或者需要返回任務(wù)執(zhí)行結(jié)果,就需要我們自己維護(hù)很多額外的代碼,而AsyncTask是基于concurrent架包提供的并發(fā)類實現(xiàn)的,上面的二個需求都已經(jīng)幫我們封裝了,這也是我們選擇AsyncTask的原因。
怎么用AsyncTask
我們還是簡單介紹下AsyncTask一些使用示例。我們先新建一個類DemoAsyncTask繼承AsyncTask,因為AsyncTask是抽象類,其中doInBackground方法必須重寫。
private class DemoAsyncTask extends AsyncTask<String, Void, Void> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(String... params) { return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); } @Override protected void onCancelled(Void aVoid) { super.onCancelled(aVoid); } @Override protected void onCancelled() { super.onCancelled(); } } DemoAsyncTask task = new DemoAsyncTask(); task.execute("demo test AsyncTask"); //task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, "test"); //myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "test");
簡單分析下
上面就是AsyncTask最簡單的使用方法,我們上面重寫的方法中,onInBackground方法運行在工作線程,其他的方法全部運行在主線程,另外它的運行方式Android提供給我們2個方法,上面都列出了。
1.第一個方法會使用默認(rèn)的Executor執(zhí)行我們的任務(wù), 其實也就是SERIAL_EXECUTOR,SERIAL_EXECUTOR我們其實也是可以通過方法去自定義的,Android幫我們的默認(rèn)實現(xiàn)是逐個執(zhí)行任務(wù),也就是單線程的,關(guān)于AsyncTask的任務(wù)執(zhí)行是單線程實現(xiàn)還是多線程實現(xiàn)還有一段很有意思的歷史,較早的版本是單線程實現(xiàn),從Android2.X開始,Google又把它改為多線程實現(xiàn),后來Google發(fā)現(xiàn),多線程實現(xiàn)的話,會有很多需要保證線程安全的額外工作留給開發(fā)者,所以從Android3.0開始,又把默認(rèn)實現(xiàn)改為單線程了,今天我們演示的是Framwork代碼版本是21(Android5.0)。
2.同時也提供了多線程實現(xiàn)的接口,即我們上面寫的最后一行代碼 AsyncTask.THREAD_POOL_EXECUTOR。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
其實相信大家平時工作學(xué)習(xí)中經(jīng)常使用AsyncTask,我們直接去看AsyncTask類源碼(插一句題外話,平時大家也可以把自己工作學(xué)習(xí)中的心得體會總結(jié)一下,記下來~~)
public abstract class AsyncTask<Params, Progress, Result> { .... }
AsyncTask抽象類,有三個泛型參數(shù)類型,第一個是你需要傳遞進(jìn)來的參數(shù)類型,第二個是任務(wù)完成進(jìn)度的類型一般是Integer,第三個是任務(wù)完成結(jié)果的返回類型,有時這些參數(shù)不是全部需要,不需要的設(shè)為Void即可,另外Result只有一個,但Params可以有多個。
我們可以看到AsyncTask的默認(rèn)構(gòu)造器初始化了二個對象,mWorker和mFuture。
private final WorkerRunnable<Params, Result> mWorker; private final FutureTask<Result> mFuture; mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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 occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
mWoker實現(xiàn)了Callback接口,Callback接口是JDK1.5加入的高級并發(fā)架包里面的一個接口,它可以有一個泛型返回值。
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
FutureTask實現(xiàn)了RunnableFuture接口,RunnableFuture接口是JDK提供的,看名稱就知道它繼承了Runnable和Future接口,mFuture是FutureTask的一個實例,可以直接被Executor類實例execute。我們來繼續(xù)看AsyncTask的execute方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } 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; }
先調(diào)用onPreExecute()方法,此時還在主線程(嚴(yán)格來說是調(diào)用AsyncTask執(zhí)行的線程),然后exec.execute(mFuture),把任務(wù)交給exec處理,execute mFuture其實就是invoke mWoker,然后調(diào)用postResult(doInBackground(mParams)),此時已經(jīng)運行在工作線程池,不會阻塞主線程。然后給mHandler發(fā)送MESSAGE_POST_RESULT消息,然后調(diào)用finish方法,如果isCancelled,回調(diào)onCancelled,否則回調(diào)onPostExecute。
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } private static final InternalHandler sHandler = new InternalHandler(); private static class InternalHandler extends Handler { @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; } } } private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
現(xiàn)在其實我們已經(jīng)把AsyncTask整個執(zhí)行任務(wù)的過程走完了,其中暴露給我們的那幾個回調(diào)方法也都走到了?,F(xiàn)在我們回過頭來看,AsyncTask其實只是對JDK 1.5提供的高級并發(fā)特性,concurrent架包做的一個封裝,方便開發(fā)者來處理異步任務(wù),當(dāng)然里面還有很多細(xì)節(jié)處理的方法值得大家學(xué)習(xí),如任務(wù)執(zhí)行進(jìn)度的反饋,任務(wù)執(zhí)行原子性的保證等,這些留給大家自己學(xué)習(xí)了。
源碼分析
下面我們再深入一些,來看AsyncTask的源碼。下面分析這個類的實現(xiàn),主要有線程池以及Handler兩部分。
線程池
當(dāng)執(zhí)行一個AsyncTask的時候調(diào)用的是execute()方法,就從這個開始看:
public final AsyncTask<Params, Progress, Result> execute(Params... params){ return executeOnExecutor(sDefaultExecutor, params); } 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; //先執(zhí)行 onPreExecute onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }
execute方法會調(diào)用executeOnExecutor。在這個方法中先檢查任務(wù)是否已經(jīng)執(zhí)行或者執(zhí)行結(jié)束,然后把任務(wù)標(biāo)記為running。最開始執(zhí)行的是onPreExecute,接著把參數(shù)賦值給mWorker對象。這個mWorker是一個Callable對象,最終被包裝為FutureTask,代碼如下:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; 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 occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
從上面的代碼可以看出,在mWorker對象中的call()方法會調(diào)用doInbackground,返回值交給postResult方法,這個方法通過Handler發(fā)送消息,這一點稍后再詳細(xì)分析。
在mWorker對象被封裝成FutureTask之后交由線程池執(zhí)行,從execute方法可以看出,使用的是sDefaultExecutor,它的值默認(rèn)為SERIAL_EXECUTOR,也就是串行執(zhí)行器,實現(xiàn)如下:
private static class SerialExecutor implements Executor { //線性雙向隊列,用來存儲所有的AsyncTask任務(wù) final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); //當(dāng)前正在執(zhí)行的AsyncTask任務(wù) Runnable mActive; public synchronized void execute(final Runnable r) { //將新的AsyncTask任務(wù)加入到雙向隊列中 mTasks.offer(new Runnable() { public void run() { try { //執(zhí)行AsyncTask任務(wù) r.run(); } finally { //當(dāng)前任務(wù)執(zhí)行結(jié)束后執(zhí)行下一個任務(wù) scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { //從任務(wù)隊列中取出隊列頭部的任務(wù),如果有就交給并發(fā)線程池去執(zhí)行 if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } } public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
在上面的代碼中,如果有任務(wù)執(zhí)行,那么SerialExecutor的execute方法會被調(diào)用,它的邏輯是把Runnable對象加入ArrayDeque隊列中,然后判斷mActivie是否為空。第一次執(zhí)行時mActive當(dāng)然為空,所以執(zhí)行scheduleNext,其實就是取出任務(wù)隊列中的第一個任務(wù)交給線程池(THREAD_POOL_EXECUTOR)執(zhí)行。加入mTask隊列的Runnable對象的run方法里最終一定會調(diào)用scheduleNext,那么又會從任務(wù)隊列中取出隊頭任務(wù)執(zhí)行。這樣便實現(xiàn)了單線程順序執(zhí)行任務(wù),所以在AsyncTask中默認(rèn)啟用的是單線程執(zhí)行,只有上一個任務(wù)執(zhí)行后才會執(zhí)行下一個任務(wù)。如果想要啟用多線程執(zhí)行任務(wù),可以直接調(diào)用 executeOnExecutor(Executor exec, Params... params),這里的Executor參數(shù)可以使用AsyncTask自帶的THREAD_POOL_EXECUTOR,也可以自己定義。
Handler
AsyncTask內(nèi)部用Handler傳遞消息,它的實現(xiàn)如下:
private static class InternalHandler extends Handler { @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; } } }
如果消息類型是任務(wù)執(zhí)行后的返回值(MESSAGE_POST_RESULT)將調(diào)用finish()方法:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
從上面可以知道,如果任務(wù)取消了,將調(diào)用onCancelled,否則調(diào)用onPostExecute,所以一個AsyncTask任務(wù)如果取消了,那么onPostExecute將不會得到執(zhí)行。
如果消息類型是執(zhí)行進(jìn)度(MESSAGE_POST_PROGRESS)將調(diào)用onProgressUpdate,這個方法默認(rèn)是空方法,我們可以根據(jù)自己的需要重寫。
總結(jié)
AsyncTask的主要邏輯就如上面所分析的,總結(jié)幾個需要注意的地方:
- AsyncTask的類必須在UI線程加載(從4.1開始系統(tǒng)會幫我們自動完成)
- AsyncTask對象必須在UI線程創(chuàng)建
- execute方法必須在UI線程調(diào)用
- 不要手動調(diào)用onPreExecute()、doInBackground、onProgressUpdate方法
- 一個任務(wù)只能被調(diào)用一次(第二次調(diào)用會拋出異常)
相關(guān)文章
Android 通過jni返回Mat數(shù)據(jù)類型方法
今天小編就為大家分享一篇Android 通過jni返回Mat數(shù)據(jù)類型方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Android下拉刷新ListView——RTPullListView(demo)
下拉刷新已經(jīng)形成一種默認(rèn)的用戶習(xí)慣,今天主要介紹下在Android上實現(xiàn)下拉刷新的Demo,感興趣的朋友可以參考下哈,希望可以幫助到你2013-04-04Android中Listview點擊item不變顏色及設(shè)置listselector 無效的解決方案
這篇文章主要介紹了Android中Listview點擊item不變顏色及設(shè)置listselector 無效的原因及解決方案,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09Android自定義狀態(tài)欄顏色與應(yīng)用標(biāo)題欄顏色一致
看IOS上的應(yīng)用,應(yīng)用中狀態(tài)欄的顏色總能與應(yīng)用標(biāo)題欄顏色保持一致,用戶體驗很不錯,對于這種效果怎么實現(xiàn)的呢?下面小編給大家分享android自定義狀態(tài)欄顏色與應(yīng)用標(biāo)題欄顏色一致的實現(xiàn)方法,一起看看吧2016-09-09