Android入門教程 | AsyncTask 使用介紹
AsyncTask 有助於使用 UI 執行緒。這個類能讓你不主動使用多執行緒或 Handler,在子執行緒種執行耗時任務,並在UI執行緒釋出結果。
AsyncTask 是一個在不需要開發者直接操作多執行緒和 Handler 的情況下的幫助類,適用於短時間的操作(最多幾秒)。 如需長時間的執行緒操作,建議使用多執行緒包
java.util.concurrent
中的功能,比如執行緒池。
假設我們有個需要在後臺執行緒中執行的非同步計算任務,並且結果需要更新 ui。 那我們需要關注3個範型引數:
Params
,
Progress
和
Result
。
再關注4個步驟方法:
onPreExecute
,
doInBackground
,
onProgressUpdate
和
onPostExecute
屬性介紹
使用 AsyncTask 之前,我們先看一下它的三個型別。
AsyncTask<Params, Progress, Result>
屬性 | 描述 |
---|---|
Params |
執行任務前,傳入的引數的型別 |
Progress |
後臺執行緒執行的時候,用來表示進度的型別 |
Result |
表示執行結果的型別 |
這 3 個型別需要開發者自己指定。比如指定 String, Integer 等。這 3 個型別在後面的方法裡會用到。
不用的泛型可以用
Void
表示。例如
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
方法介紹
要使用 AsyncTask ,必須新建一個類來繼承它,並且重寫
doInBackground
方法。通常也會重寫
onPostExecute
方法。 執行非同步任務的時候,我們主要關心下面這4個方法。
方法 | 描述 |
---|---|
onPreExecute() |
執行任務前在ui執行緒呼叫。通用用來設定任務,比如在介面上顯示一個進度條。 |
Result doInBackground(Params... params) |
在
onPreExecute() 結束後立即呼叫這個方法。耗時的非同步任務就在這裡操作。執行任務時傳入的引數會被傳到這裡。非同步任務的中間結果在這裡可以用
publishProgress 傳送到主執行緒。 |
onProgressUpdate(Progress... values) |
在 ui 執行緒中執行。後臺任務還在進行的時候,這裡負責處理進度資訊。比如在這顯示進度條動畫,修改文字顯示等。 |
onPostExecute(Result result) |
後臺任務結束了調這個方法。它在 ui 執行緒執行。最後的結果會傳到這。 |
AsyncTask 的三種狀態
每個狀態在一個任務的生命週期中只會被執行一次。
狀態 | 描述 |
---|---|
PENDING | 等待(還沒有開始執行任務) |
RUNNING | 執行中 |
FINSHED | 完成 |
用法示例
虛構一個計算任務
/** * 虛擬的計算任務 */private class CalculationTask extends AsyncTask<Float, Integer, Float> { protected Float doInBackground(Float... inputs) { Log.d(TAG, "doInBackground thread ID = " + Thread.currentThread().getId()); long step = 0; float result = 0; for (float f : inputs) { // 假設這裡有一些耗時的操作 result += f; } while (step < 5) { result += step; step++; publishProgress((int) step); } return result; } protected void onProgressUpdate(Integer... progress) { Log.d(TAG, "onProgressUpdate thread ID = " + Thread.currentThread().getId()); Log.d(TAG, "onProgressUpdate: " + progress[0]); } protected void onPostExecute(Float result) { Log.d(TAG, "onPostExecute thread ID = " + Thread.currentThread().getId()); Log.d(TAG, "任務執行完畢"); } }// 執行任務new CalculationTask().execute(1.2f, 2.3f, 6.3f);
取消任務
呼叫
cancel(boolean)
可隨時取消任務。取消任務後
isCancelled()
會返回true。
呼叫這個方法後,後臺任務
doInBackground(Object[])
執行完畢後會呼叫
onCancelled(Object)
而不再是
onPostExecute(Object)
。 為保證任務能被及時地取消,在
doInBackground(Object[])
中應該經常檢查
isCancelled()
返回值
執行緒規則 Threading rules
- 非同步任務必須從UI執行緒啟動
- 必須在UI執行緒例項化AsyncTask類
- 必須在UI執行緒呼叫
execute(Params...)
- 不要手動呼叫
onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)
- 同一個非同步任務例項只能被執行一次。重複執行同一個非同步任務例項會丟擲異常(
IllegalStateException
)。
AsyncTask 相關面試題
1. AsyncTask 是什麼?能解決什麼問題
2. 談談 AsyncTask 的三個泛型引數作⽤
3. 說說AsyncTask的原理
構造⽅法中建立了⼀個 WorkRunnable 和⼀個 FutureTask 物件,
在 WorkRunnable的Call ⽅法中調⽤ doInBackground ⽅法,並獲取 Result 返回值,然後返回撥⽤postResult ⽅法的返回值,建立 FutureTask 時傳⼊了 WorkRunnable 物件。
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); } }; 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); } } }; }
postResult ⽅法
getHandler⽅法獲取⾃帶Handler物件,來獲取Message
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, newAsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
這個內部⾃帶InternalHandler構造⽅法中傳⼊getMainLooper()返回值,即主執行緒Looper,
然後在handlerMessage⾥⾯針對msg.what分別執⾏了finish和onProgressUpdate⽅法,其中finish⽅法則是調⽤onCancelled或onPostExecute⽅法
private static final InternalHandler sHandler = new 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; } } }
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
看看 AsyncTask 的 execute ⽅法,返回了 executeOnExecutor 的⽅法,並傳⼊sDefaultExecutor 和 params 為引數,params 即 AsyncTask 的 doInBackground中傳⼊的 params ,
⽽ sDefaultExecutor 是預設的串⾏執⾏器(執行緒池),⼀個SerialExecutor 再看看executeOnExecutor ⽅法,先判斷了任務狀態,如果是 RUNNING 或FINISHED,則會丟擲異常,
然後會把當前狀態從 PENDING 改為 RUNNING,把⼊參 params 傳到WorkRunnable 物件中,再調⽤傳⼊的 sDefaultExecutor 的 execute ⽅法,傳⼊mFuture 為引數
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; }
execute ⽅法中調⽤ mTasks 的 offer ⽅法新增⼀個任務到快取佇列中,在 run ⽅法中就調⽤了前⾯傳⼊的 mFuture 的 run ⽅法,
然後執⾏ THREAD_POOL_EXECUTOR 的 execute ⽅法,任務的實際執⾏就在這,THREAD_POOL_EXECUTOR 就是⼀個執行緒池
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); } } }
4. 你覺得AsyncTask有不⾜之處嗎?
AsyncTask 使⽤起來⽐較輕量,但是⾃身也存在⼀些問題。
主要表現在:
- cancel ⽅法實現不是很好。如果你調⽤了 AsyncTask 的 cancel(false) ⽅法, doInBackground() 仍然會執⾏到⽅法結束,只是不會去調⽤ onPostExecute() ⽅法,但是實際上也是讓程式執⾏了沒有意義的操作。如果調⽤cancel(true),mayInterruptIfRunning 設定為 true,會使任務儘早結束,但是如果 doInBackground() 有不可被打斷的⽅法,就會失效,⽐如 BitmapFactory.decodeStream() 操作。
- 記憶體洩露,在 Activity 中使⽤⾮靜態匿名內部類 AsyncTask 類,由於 Java 內部類的特點,內部類持有外部類引⽤,⽽由於 AsyncTask ⽣命週期可能⽐ Activity 的,當 Activity 銷燬時,AsyncTask 還在執⾏,由於AsyncTask 持有 Activity 的引⽤,導致 Activity 物件⽆法回收,進⽽產⽣記憶體洩露。
- 結果丟失,當螢幕旋轉等造成 Activity 新建立時 AsyncTask 資料丟失的問題。當 Actviity 銷燬並建立新的收,還在運⾏的 AsyncTask 會持有⼀個 Activity 的⾮法引⽤,即之前 Activity 的例項,導致onPostExecute() ⽅法⽆效。
- 串⾏,並⾏多版本不⼀致.1.6之前為串⾏,1.6-2.3為並⾏,3.0之後⼜改為串⾏,但是可以透過executeOnExecutor() 實現並⾏處理
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70008155/viewspace-2846330/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android入門教程 | mmap 檔案對映介紹Android
- Android入門教程 | SharedPreferences 簡介Android
- Android入門教程 | RecyclerView使用入門AndroidView
- gitbook 入門教程之外掛介紹Git
- Android入門教程 | DialogFragment 的使用AndroidFragment
- Android測試工具 UIAutomator入門與介紹AndroidUI
- MapStruct的介紹及入門使用Struct
- Android入門教程 | RecyclerView實際使用AndroidView
- DQN(Deep Q-learning)入門教程(零)之教程介紹
- Android入門教程 | EditText 使用者輸入Android
- 新手入門必備:kylin安裝教程介紹!
- Linux入門教程之sed 命令常用操作介紹Linux
- Mybatis 入門介紹MyBatis
- Rocketmq 入門介紹MQ
- libevent入門介紹
- Django 入門介紹Django
- linux介紹(入門)Linux
- Nginx 入門介紹Nginx
- rollup入門介紹
- Vue入門到關門之Vue介紹與使用Vue
- IOS 初級開發入門教程(一)介紹篇iOS
- 網路流量預測入門(零)之教程介紹
- ccs的介紹,安裝和使用入門
- Android入門教程 | TextView簡介(寬高、文字、間距)AndroidTextView
- Spring Shell入門介紹Spring
- Android 非同步載入——AsyncTask詳談Android非同步
- Android入門教程 | Kotlin協程入門AndroidKotlin
- Android 輸入系統介紹Android
- Tomcat 介紹及使用教程Tomcat
- 第二章 Redis API的使用 單執行緒介紹【Redis入門教程】RedisAPI執行緒
- WebSocket協議入門介紹Web協議
- 『python入門:』 python的介紹Python
- 整合學習入門介紹
- Android入門教程 |res資源目錄簡介與shape的繪製和使用Android
- Android入門教程 | Handler,Looper與MessageQueue使用與分析AndroidOOP
- Android 動畫 介紹與使用Android動畫
- Python - pydantic 入門介紹與 Models 的簡單使用Python
- 【Android開發入門教程】三.Activity入門指南!Android