Android中的AsyncTask的工作原理
Android中的執行緒機制是非常重要的,在很多情況下為了使APP不卡頓,我們需要將很多事情放到子執行緒中執行,使主執行緒儘量沒有耗時操作,否則會導致ANR.Android中的執行緒幾乎完全採用了Java中的執行緒機制,那麼建立、銷燬、排程執行緒對執行緒的瞭解就很重要了.
Runnable/Callable
如何建立一個執行緒我相信只要做過Java的都會建立.這裡就簡單介紹一下.
new Thread(){ @Override public void run() { super.run(); } }; new Thread(new Runnable() { @Override public void run() { } });
需要注意的是
Runnable 和 Callable,它們兩個都是執行緒中執行的任務,主要區別是Callable可以在任務完成時能夠返回一個值,Callable 可以返回裝載有計算結果的 Future 物件.它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過 Future 物件可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果;Runnable是執行工作的獨立任務,它不返回任何值.
Thread類只支援Runnable介面,只有執行緒池支援Callable
下面我們通過一個例子,看一下Callable
private static void callable() { ExecutorService executorService = Executors.newCachedThreadPool(); List<Future<String>> futureList = new ArrayList<>(); for (int i = 0; i < 10; i++) { //submit 會產生Future物件 它用Callable返回的結果的特定型別進行了引數化 //可以用future.isDone來查詢future是否已經完成 直接呼叫future.get可能會阻塞 知道結果準備就緒 futureList.add(executorService.submit(new TaskWithResult(i))); } for (Future<String> future : futureList) { try { if (future.isDone()) { System.out.println(future.get()); } } catch (InterruptedException | ExecutionException e) { System.out.println(e); } finally { executorService.shutdown(); } } } public class TaskWithResult implements Callable<String> { private int id; public TaskWithResult(int id) { this.id = id; } @Override public String call() throws Exception { return "TaskWithResult is id:" + id; } }
Thread 也可以實現這種方式,FutureTask
關於FutureTask
FutureTask
實現了Runnable, Future<V>
,它既可以被Thread使用,也可以被Executor使用.
Future
彌補了執行緒設計的不足,他可以讓你知道執行緒是否執行完畢,並且執行完畢後返回的結果.可以取消的非同步的計算任務,get獲取結果會阻塞等待直到結果準備就緒
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
我們繼續通過一個小例子看一下FutureTask如何工作的.
FutureTask<String> futureTask = new FutureTask<String>(new Task()) { @Override protected void done() { try { String s = get(); System.out.print("result:" + s); } catch (ExecutionException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }; ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(futureTask); try { //阻塞 直到獲得執行結果 futureTask.get(); } catch (Exception e) { e.printStackTrace(); } static class Task implements Callable<String> { @Override public String call() throws Exception { //耗時操作 Thread.sleep(3000); return "ok"; } }
FutureTask
在Android中充當執行緒的角色還有AsyncTask
、HandlerThread
、IntentService
等,下面通過AsyncTask來看看它是如何使用執行緒的
AsyncTask使用方法
什麼是AsyncTask?這裡我就不細說了,只要做過Android開發的都不陌生,AsyncTask它是一個輕量級的非同步任務,內部封裝了Thread和Handler,可以讓我們在後臺進行計算並且把計算的結果及時更新到UI上
AsyncTask使用的例子
private class Task extends AsyncTask<String, Integer, String> { @Override protected String doInBackground(String... strings) { return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(String s) { super.onPostExecute(s); } @Override protected void onPreExecute() { super.onPreExecute(); } } new Task().execute("123");
使用AsyncTask需要注意的地方:
- AsyncTask的類必須在UI執行緒載入(從4.1開始系統會幫我們自動完成)
- AsyncTask物件必須在UI執行緒建立
- execute方法必須在UI執行緒呼叫
- 不要在你的程式中去直接呼叫onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法
- 一個AsyncTask物件只能執行一次,即只能呼叫一次execute方法,否則會報執行時異常
- AsyncTask不是被設計為處理耗時操作的,耗時上限為幾秒鐘,如果要做長耗時操作,強烈建議你使用Executor,ThreadPoolExecutor以及FutureTask
- 在1.6之前,AsyncTask是序列執行任務的,1.6的時候AsyncTask開始採用執行緒池裡處理並行任務,但是從3.0開始,為了避免AsyncTask所帶來的併發錯誤,AsyncTask又採用一個執行緒來序列執行任務
AsyncTask原始碼解析(API 28)
分析AsyncTask工作原理,我們首先看AsyncTask的構造方法
public AsyncTask(@Nullable Looper callbackLooper) { //得到一個handler 這也是為什麼AsyncTask 必須在主執行緒中呼叫,因為子執行緒中預設沒有Looper無法建立 //Handler mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); //WorkerRunnable 實現了Callable 可以將任務的結果返回 mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { //標記任務被執行 mTaskInvoked.set(true); Result result = null; try { //設定執行緒的優先順序 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked //呼叫doInBackground 進行耗時操作比如網路請求 如果需要進度則需要在doInBackground方法中 //計算進度 然後傳遞給 result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { //標記任務被取消 mCancelled.set(true); throw tr; } finally { //得到結果 返回給 onPostExecute postResult(result); } //將得到的結果返回 return result; } }; //FutureTask 進一步判斷,如果任務沒有被標記,那麼將得到結果返回給 onPostExecute 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); } } }; }
上述程式碼,在建立AsyncTask的時候,首先建立一個Handler,通過Handler將子執行緒切換到主執行緒中執行,這也就是為什麼說建立AsyncTask必須在主線衝中建立.
WorkerRunnable
實現了Callable,同時我們還看到了FutureTask
,我們著重看一下WorkerRunnable做了些什麼?內部呼叫了doInBackground(mParams);
執行任務,並將任務的結果返回postResult
.
postResult()方法如果將結果返回的?
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }
在上面的程式碼中,給Handler傳送了一個訊息,在主執行緒返回結果
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; } } } private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
在上面的程式碼中,呼叫了onPostExecute(result)
方法,將結果返回,交給開發人員處理.通過上述程式碼,我們知道了AsyncTask是如何執行Runnable並且將結果返回的.
下面分析執行緒池是如何執行Runnable的,先從execute
方法看起,如下所示:
@MainThread 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; }
在上述的程式碼中,可以看出sDefaultExecutor
是一個執行緒池,並通過sDefaultExecutor執行mFuture.
sDefaultExecutor是如何實現的,如下所示
//序列任務執行器,其作用就是將多個任務一個一個的執行(execute()) public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //預設的任務執行器,預設為序列任務執行 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; //序列執行器的實現,AsyncTask.execute(param...) 方法最終是走到這裡 //其實現就是將任務加入到佇列中 一個個排隊 去實現任務 private static class SerialExecutor implements Executor { //線性雙向佇列 用來儲存所有的AsyncTask任務 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); //正在執行的任務 Runnable mActive; public synchronized void execute(final Runnable r) { //序列執行多個任務核心程式碼 //將新的AsyncTask任務加入到雙向佇列中 mTasks.offer(new Runnable() { public void run() { try { //執行任務 r.run(); } finally { //當AsyncTask任務執行完畢後,執行下一個任務 scheduleNext(); } } }); //如果沒有正在執行的任務 從任務佇列中直接獲取任務 執行 if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { //取出佇列頭部任務 交給執行緒池執行 if ((mActive = mTasks.poll()) != null) { //執行緒池執行該任務 THREAD_POOL_EXECUTOR.execute(mActive); } } }
從上述程式碼中,分析AsyncTask執行的任務是序列執行的過程,首先AsyncTask將FutureTask交給SerialExecutor
的execute方法去處理,SerialExecutor
的execute方法會將FutureTask插入到mTasks(雙向佇列)中,如果沒有正在執行的任務mActive==null
從任務佇列中直接獲取任務 執行scheduleNext
知道所有的任務執行完為止.
如何實現並行任務呢?
通過 executeOnExecutor 可自定義AsyncTask的執行方式,序列or並行,execute是序列執行,或者自定義執行緒池asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params);
可實現並行執行任務
關於任務進度
//需要在doInBackground 方法中獲取實際的進度 然後呼叫publishProgress @WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
生產/消費模式
定義生產者
public class Producer extends Thread { private final Object mutex; public Producer(Object mutex) { this.mutex = mutex; } @Override public void run() { //生產產品 while (true) { synchronized (mutex) { //如果生產的產品沒有被消費掉 等待消費者消費 if (null != Product.value) { try { mutex.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //使用 if (null == Product.value) { Product.value = "NO." + System.currentTimeMillis(); System.out.println("生產產品:" + Product.value); } //通知消費者可以消費了 mutex.notify(); } } } }
定義消費者
public class Consumer extends Thread { private final Object mutex; public Consumer(Object mutex) { this.mutex = mutex; } @Override public void run() { while (true) { synchronized (mutex){ //如果沒有產品生產就等待 if (null == Product.value){ try { mutex.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //有產品生產了 if (null != Product.value) { System.out.println("消費產品:" + Product.value); Product.value = null; } //通知生產者產品已經消費 mutex.notify(); } } } }
定義一個產品
public class Product { //執行緒機制為了提高執行效率,當一個執行緒不斷當訪問 一個變數 //執行緒會使用一個私有空間 儲存這個變數 //volatile 易變變數 專門修飾被不同執行緒訪問和修改的變數 //讓執行緒訪問這個變數 public volatile static String value; }
實現方法
//Object mutex = new Object(); //new Producer(mutex).start(); //new Consumer(mutex).start();