原始碼分析 —— AsyncTask 完全解析(基於7.0)

weixin_34402408發表於2018-01-03

在平常開發中執行非同步操作很常見的操作,經常在執行非同步任務後需要去重新整理 UI,這就需要用到 Android 的非同步訊息處理機制 Handler, 當然我們也可以使用 AsyncTask 去完成,相信大家或多或少都用過 AsyncTask 。

基本用法:

三個泛型 : AsyncTask < Params, Progress, Result >

 1. Params : 啟動任務執行傳入的引數, 例如HTTP請求的URL , 執行任務需要的資料型別
 2. Progress :  後臺執行任務進度的進度型別
 3. Result :  非同步任務最終返回的結果型別
複製程式碼

幾個常用方法:

  • protected void onPreExecute() {}

    • 執行在UI執行緒, 一般在執行非同步任務前做一些UI操作
  • protected Result doInBackground(Params ... params) {}

    • 執行在非同步執行緒, 耗時的操作在這個方法中執行。 返回結果被傳遞到 onPostExecute(), 在執行任務時呼叫publishProgress() 把執行進度傳遞給 onProgressUpdate()
  • protected void onProgressUpdate(Progress ... progresses) {}

    • 執行在UI執行緒, 更新進度資訊, 呼叫 publishProgress() 時被回撥
  • protected void onPostExecute(Result result) {}

    • 執行在UI執行緒, 一般在執行完後臺任務後更新UI的操作, 顯示結果等
  • protected void onCancelled() {}

    • 執行在UI執行緒, 呼叫 cancel() 被呼叫

原始碼流程

具體用法就不再這裡展示了,接下來我們直接看原始碼,首先看 new AsyncTask() 時候構造方法裡面做了什麼:

構造方法

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
           ...省略
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
		  ...省庫
        }
    };
}
複製程式碼

2 行: mWorkerWorkerRunnable 型別的內部類物件:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

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;
}
複製程式碼

分析繼承關係,最終 WorkerRunnable 實現了 Callable 介面,介面中只有一個 call() 方法返回值型別是泛型。因為不論是繼承 Thread 類還是實現 Runnable 方法,執行完任務以後都無法直接返回結果。而Callable介面彌補了這個缺陷,當call()方法執行完畢以後會返回一個泛型物件。mWorker 重寫了 call 方法,具體實現先不看,等呼叫的時候再具體分析

接著第 8 行: FutureTask ,它實現了 RunnableFuture 介面, RunnableFuture 最終繼承 RunnableFuture 介面

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}
複製程式碼

Runnable 介面我們熟悉, 說明 FutureTask 是可以被 Thread 執行的, Future<V> 介面主要是針對Runnable 和 Callable 任務的。提供了三種功能:

  1. 判斷任務是否完成 ;
  2. 中斷任務 ;
  3. 獲取任務執行的結果

**mAsyncTask().execute(); **

接著我們從執行非同步任務的起點開始,進入 execute 方法:

##mAsyncTask
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;
}
複製程式碼

5行:判斷任務在執行當中或者已經執行完成, 則報出異常,即每個任務只能執行一次
17行:執行了 onPreExecute() , 當前還在主執行緒
18行:用 mWorker.mParams 儲存傳入的引數
19行:呼叫引數 Executor 型別的 exec 的 execute() 方法, 並把 mFuture 傳進去,exec 是在 execute() 方法中傳的 sDefaultExecutor。

sDefaultExecutor 是 SerialExecutor 類的例項, SerialExecutor 內部維護了一個 ArrayDeque 型別的佇列 mTasks

##mAsyncTask
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);
        }
    }
}
複製程式碼

7-12 行:封裝傳入進來的 mTask 新增到 mTasks 隊尾,可以看到在 run() 方法中,執行 mTaskrun() 方法後一定會執行 scheduleNext()
16-17 行:判斷 Runnable 型別的 mActive 是否為空,第一次執行肯定為空,則呼叫 scheduleNext()
21-22 行:取出隊首的元素賦給 mActive ,不為空則讓執行緒池去執行這個任務

  • 從上面可以分析出來,當佇列中有任務時,任務的執行順序是當佇列中前一個任務執行完成後,後面如果有任務則繼續取出並讓 THREAD_POOL_EXECUTOR 執行緒池去執行,執行完成後繼續佇列中的後一個任務,知道佇列中沒有元素為止。而重複執行 AsynscTask.execute() 並不會多次觸發執行緒池,只有佇列中沒有任務時,才會觸發執行緒池執行新增進來的任務。所以,安卓7.0下的 AsyncTask 任務執行是序列的,相當於單執行緒池的效果。

我們繼續看任務的執行,即執行緒池去執行 mTaskrun() 方法:

##FutureTask
public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable; 
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call(); 
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result); 
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
複製程式碼

6 行:callable 就是 FutureTask 構造方法中傳入進來的 mWorker,然後賦值給 c
11 行:執行 mWorker 的 call() 方法,並得到執行結果 result , 而 mWorkder 在 AynscTask 構造方法中重寫了 call() 方法

##AsyncTask
public AsyncTask() {
    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
                result = doInBackground(mParams); 
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result); 
            }
            return result;
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {...}
}
複製程式碼

4 行:標記位 mTaskInvoked 設定為 true
7 行:設定執行緒優先順序 為10。優先順序分為 1-10,數值越大優先順序越高,預設是5。
9 行:出現了 doInBackground() , 此時方法呼叫在 mTask 的 run() 中,即在非同步執行緒中執行 doInBackground() 並得到返回結果 result
15 行:postResult(result),字面意思是把結果 post 出去。方法實現先不看,因為後面也呼叫了此方法再具體分析

繼續看 FutureTask run() 方法的 19 行:set(result)

##FutureTask 
protected void set(V v) {
    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
        outcome = v; 
        U.putOrderedInt(this, STATE, NORMAL);
        finishCompletion();
    }
}
複製程式碼

3 行:用 outcome 儲存執行結果
5 行: finishCompletion():

##FutureTask 
private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
        if (U.compareAndSwapObject(this, WAITERS, q,
            for (;;) {
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    LockSupport.unpark(t);
                }
                WaitNode next = q.next;
                if (next == null)
                    break;
                q.next = null; // unlink to help gc
                q = next;
            }
            break;
        }
    }
    done();
    callable = null;        // to reduce footprint
}
複製程式碼

直接看倒數第二行 done() 方法,是不是很眼熟?還記得 AsyncTask 構造方法中 mTask 重寫了 done() 方法嗎

##AsyncTask
public AsyncTask() {
    mWorker = new WorkerRunnable<Params, 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);
            }
        }
    };
}

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}
複製程式碼

在 mTask 重寫的 done() 方法中, 把 get() 獲取的任務執行結果傳遞給 postResultIfNotInvoked() 方法,在方法中:如果 mTaskInvoked 不為true,則執行 postResult ; 但是在 mWorker 初始化時就已經 將 mTaskInvoked 為 true,所以一般這個 postResult 執行不到。所以這個 postResult 一般是在 mWorker 執行結束 call() 時執行

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,   
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
複製程式碼

可以看到 postResult 中出現了我們熟悉的 Handler 訊息機制,傳遞了一個訊息 message, message.what = MESSAGE_POST_RESULT;message.object = new AsyncTaskResult(this,result);

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;
    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}
複製程式碼

AsyncTaskResult 就是對 AsyncTask 和 result 資料的簡單封裝。
很容易猜測出來 sHandler 肯定重寫了 handleMessage() 方法去接收這個訊息並處理:

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;
        }
    }
}
複製程式碼

3 行:說明是主執行緒的 Handler
9 行:msg.what 分兩種 MESSAGE_POST_RESULT 和 MESSAGE_POST_PROGRESS,即結果 result 和 進度progress,這裡先看 result 的處理

private void finish(Result result) {
    if (isCancelled()) {    
        onCancelled(result);  
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
複製程式碼

如果任務被 cancel() 則呼叫 onCancelled(),正常執行完呼叫 onPostExecute(),此時在 Handler 的 handleMessage 中,因此是主執行緒執行,最後將狀態置為 FINISHED

在日常開發中一個常用操作就是在 doInBackgroud 中呼叫 publishProgress 把執行進度更新給 onProgressUpdate,然後在 onProgressUpdate 根據執行進度來做一些 UI 處理,比如顯示進度條之類的:

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget(); 
    }
}
複製程式碼

還是通過 sHandler 傳送一個 message , what 是 MESSAGE_POST_PROGRESS

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;
    }
}
複製程式碼

在 sHandler 的 handleMessage 中,回撥了 onProgressUpdate ,明顯執行在主執行緒

到這裡,整個 AsyncTask 的執行流程就結束了,看起來好像方法東跳來西跳去有點暈,其實自己對著原始碼多走幾遍就很清晰了 下面有個比較清晰的流程圖,借用自 www.jianshu.com/p/e60c3bb03…

cancel()

此外,在 AsyncTask 中有一個小坑要注意一下,對於 cancel 方法的呼叫:

 * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
 *        task should be interrupted; otherwise, in-progress tasks are allowed
 *        to complete.
 */
public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);   // 標誌位 mCancelled 設定為 true
    return mFuture.cancel(mayInterruptIfRunning);
}
複製程式碼

從官方註釋來看,mayInterruptIfRunning 如果為 true 當前非同步任務就會被終止,否則該任務會執行完成。而實際開發中,呼叫 cancel 並不能中斷當前執行的任務。

通過原始碼可以看到,方法內部只是給 AsyncTask 設定一個 "canceled" 狀態,然後向執行中的執行緒發出 interrupt() 呼叫。在這種情況下,你的執行緒是不可中斷的,也就不會終止該執行緒。所以,這就需要你主動去檢查 AsyncTask 是否已經取消,之後決定是否終止你的操作。因此需要這樣操作

public void onProgressUpdate(Integer... value) {
	...
	if(isCancelled()) 
		return;
	...
}

@Override
protected Integer doInBackground(Void... mgs) {
	...
	if(isCancelled()) 
		return null;
	...
}
...`
複製程式碼

另外在需要結束 AsyncTask 的地方:

if(task != null && !task.isCancelled() && task.getStatus() == AsyncTask.Status.RUNNING) {
	task.cancel(true);  
	task = null;  
}
複製程式碼

相關文章