AsyncTask的使用方式和版本演進
AsyncTask是什麼?
AsyncTask這個類允許在後臺執行操作並在UI執行緒上更新結果而不用必須操縱主執行緒或使用handlers;
AsyncTask作為 Thread Handler輔助類存在,並不構成通用的執行緒框架。
理想情況下,AsyncTasks應該是用於短操作(最多幾秒鐘。)如果你需要保持執行緒長時間執行,強烈建議使用各種API
concurrent包提供的API例如:ThreadPoolExecutor和FutureTask。
AsyncTask怎麼使用?
-
AsyncTask 使用的時候必須子類化,即繼承這個類才能使用;
-
需要觸發時呼叫execute()方法。
-
繼承時需要設定三個泛型 Params , Progress 和 Result 的型別,如AsyncTask<Void,Inetger,Void>;
其中:
Params: 指呼叫 execute() 方法時傳入的引數型別和 doInBackgound() 的引數型別。
Progress: 在後臺請求資料期間釋出的進度單元的型別。
Result: 非同步執行緒請求返回的結果。
-
繼承之後的子類必須重寫doInBackground,一般還會重寫 onPostExecut 、onPreExecute 方法和 onProgressUpdate 方法。
其中:
onPreExecute: UI執行緒,執行在doInBackground之前。
doInBackground: 運行於後臺(子執行緒),耗時的操作可以在這裡做,此方法可以呼叫 publishProgress 來發布更新在UI執行緒上。
onProgressUpdate: 執行在UI執行緒中,當呼叫完 publishProgress 之後,如果當前執行緒沒有被取消,將在這個方法中更新進度。
下面是程式碼例項:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>; { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count)100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
執行的程式碼:
new DownloadFilesTask().execute(url1, url2, url3)
AsyncTask原始碼分析(API 27)
▶ 首先看一下 AsyncTask的構造方法
public AsyncTask(@Nullable Looper callbackLooper) { mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper);//初始化mhandler //初始化mWorker mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { //開始執行call方法時將mTaskInvoked置為true mTaskInvoked.set(true); Result result = null; try { //設定呼叫執行緒的優先順序為後臺執行緒 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //執行doInBackground方法(在後臺程序),並將結果返回 result = doInBackground(mParams); /** *將當前執行緒中待處理的任何Binder命令重新整理到核心/驅動程式。 * 在執行可能長時間阻塞的操作之前呼叫,以確保已釋放任何掛起的物件引用 *為了防止程序持有超過它需要的物件 */ Binder.flushPendingCommands(); } catch (Throwable tr) { //AtomicBoolean設定新值為true,false表示只允許一個執行緒訪問。 // 此處當前執行緒已經異常,故重新賦值為true,允許其他執行緒重新開始操作 mCancelled.set(true); throw tr; } finally { //傳遞結果給postResult postResult(result); } return result; } }; //初始化mFuture,傳入引數mWorker 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);//拋異常時,傳遞結果為null } } }; }
例項化了AsyncTask物件之後,我們就可以呼叫AsyncTask的execute方法執行任務
▶ execute() 方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);//呼叫executeOnExecutor方法,其中用到有個引數sDefaultExecutor值得關注 }
▶ sDefaultExecutor

sDefaultExecutor.png
具有以下兩個特點:
- 保證了不同執行緒對這個變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的。
- 禁止進行指令重排序。
那麼這個SerialExecutor 的作用是什麼?
▶ SerialExecutor
private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { //將任務新增到其內部的佇列 mTasks 中,按順序依次執行 mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { //當任務執行完畢的時候,通過呼叫scheduleNext方法執行下一個Runnable任務 scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } //並且即使在多個執行緒同時操作的情況下,由於THREAD_POOL_EXECUTOR具有可見性, //不會造成資料更新不及時的問題 protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
SerialExecutor中有一個execute 方法和 scheduleNext 方法。
execute 方法將任務新增到其內部的佇列 mTasks 中,按順序依次執行;呼叫 THREAD_POOL_EXECUTOR 處理任務,執行完畢後再從 mTasks 中取下一個任務,並且即使在多個執行緒同時操作的情況下,由於THREAD_POOL_EXECUTOR具有可見性,不會造成資料更新不及時的問題;到這裡可以回到一開始的executeOnExecutor方法。
▶ executeOnExecutor
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { //exec是之前的sDefaultExecutor 引數 if (mStatus != Status.PENDING) {//Status有三個狀態值,PENDING表示task還沒有被執行 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)"); } } //執行到這裡說明當前task需要被執行,故將狀態置為RUNNING mStatus = Status.RUNNING; //呼叫onPreExecute方法 onPreExecute(); mWorker.mParams = params;//將引數賦值給mWorker中的變數 exec.execute(mFuture);//實際呼叫的就是SerialExecutor中的execute方法 return this; }
其中的mWorker是一個實現了Callable的靜態內部抽象類,包含一個Params陣列;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
其中的mFuture是一個FutureTask<Result> ,後面FutureTask會需要一個WorkerRunnable作為引數。
到這裡execute() 方法基本結束。
最後返回結果的方法:
▶ postResult
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); //將結果傳送給handler message.sendToTarget(); return result; }
不同版本之間的差異
上面的原始碼分析基於API27,在Android 3.0之前是並沒有SerialExecutor這個類的,而是直接在AsyncTask中構建了一個sExecutor常量,並對執行緒池總大小,同一時刻能夠執行的執行緒數做了規定,而3.0之後的使用了SerialExecutor這個類,限制同一時間只能一個執行緒訪問,只能執行一個任務,執行完成之後,接著執行下一個任務。
▶ API 3.0之前
新增的任務進入多執行緒池,支援併發。
核心執行緒數為5個
最大執行執行緒數為128個
▶ API 3.0之後
預設只支援一個執行緒數;
但可以建立單獨的執行緒池;
或者使用AsyncTask.THREAD_POOL_EXECUTOR(默認同時執行執行緒數5個)
▶ AsyncTask缺點
- 開啟執行緒後,未結束,此時使用者又一次,甚至多次開啟執行緒,導致多次請求。
解決方式:將執行緒寫為靜態static。 - 當用戶開啟執行緒後,退出介面,多次進入。由於執行緒持有Activity的變數的例項,導致Activity無法被回收,從而導致記憶體洩漏
解決方式:採用弱引用的方式,將執行緒與Activity進行解耦。
參考文章 1: ofollow,noindex">https://blog.csdn.net/womengmengyan/article/details/52315564?utm_source=copy