Android 異步任務知識梳理(1) AsyncTask 源碼解析

1、概述

這篇文章中,讓咱們從源碼的角度看一下AsyncTask的原理,最後會根據原理總結一下使用AsyncTask中須要注意的點。android

2、源碼解析

AsyncTask中,有一個線程池 THREAD_POOL_EXECUTOR 和與這個線程池相關聯的 Executor,它負責執行咱們的任務Runnablebash

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {    
    private final AtomicInteger mCount = new AtomicInteger(1);    
    public Thread newThread(Runnable r) {        
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());    
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);

public static final Executor THREAD_POOL_EXECUTOR 
    = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

//這是執行Runnable的地方,調用execute會執行它,若是當前已經有一個任務在執行,那麼就是把它放到隊列當中。
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();                
                }            
             }        
        });
        //若是爲null,那麼馬上執行它;
        //若是不爲null,說明當前隊列中還有任務,那麼等Runnable執行完以後,再由上面的scheduleNext()執行它。        
        if (mActive == null) {               
            scheduleNext();        
        }    
   }    

   protected synchronized void scheduleNext() {
        //第一次進來調用了offer方法,所以會走進去,執行上面Runnable的run()方法。        
        if ((mActive = mTasks.poll()) != null) {            
           THREAD_POOL_EXECUTOR.execute(mActive);        
        }    
    }
}
複製代碼

從上面能夠看出,每次調用sDefaultExecutor.execute時就是執行一個任務,這個任務會被加入到ArrayDeque中串行執行,咱們看一下當咱們調用AsyncTaskexecute方法時,任務是如何被建立並加入到這個隊列當中的:ide

//這個靜態方法,不會回調 loadInBackground 等方法。
public static void execute(Runnable runnable) {    
    sDefaultExecutor.execute(runnable);
}

//這是咱們最常使用的方法,參數能夠由調用者任意指定。
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;
}
複製代碼

在調用了executeOnExecutor以後,咱們把傳入的參數賦值給了mWorker,並把mFuture傳入給Executor去執行,而從下面咱們能夠看到mFuture的構造函數中傳入的參數正是mWorker,這兩個東西其實才是AsyncTask的核心,它們是在AsyncTask的構造函數中被初始化的:函數

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        
        Result result = doInBackground(mParams);        
        Binder.flushPendingCommands();         
        return postResult(result);    
    }
};

mFuture = new FutureTask<Result>(mWorker) {    
    @Override    
    protected void done() {        
        try {
            //要求mWorker的call方法沒有被調用,不然什麼也不作。            
            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);        
       }    
   }
};
複製代碼

先看一下WorkerRunnable,實現了Callable<V>接口,增長了一個不定參數的成員變量用來傳給 doInBackground,這個不定參數就是咱們在execute時傳入的,調用call時會執行器內部的函數,而call 時會調用doInBackground方法,這個方法執行完以後,調用postResult,注意到calldone都是在子線程當中執行的:源碼分析

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

咱們主要看一下FutureTask,還記得最開始咱們的Executor最終執行的是傳入的Runnablerun方法,所以咱們直接看一下它的run方法中作了什麼:post

public interface Runnable {
   public void run();
}

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;
}

public interface RunnableFuture<V> extends Runnable, Future<V> { 
    void run();
}

//咱們只要關注run方法
public class FutureTask<V> implements RunnableFuture<V> {
    public void run() {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            result = c.call(); //mWorker.call()
            ran = true;
        }
        if (ran) {
            set(result);
        }
    }

    protected void set(V v) {
        finishCompletion();
    }

    private void finishCompletion() {
        done(); //mFuture.done()
        callable = null;
    }
}
複製代碼

那麼結論就清晰了,整個的執行過程是下面這樣的:ui

  • 首先調用excuter.execute(mFuture),把mFuture加入到隊列當中
  • mFuture獲得執行時,會回調mFuturerun方法
  • mFuture#run是運行在子線程當中的,它在它所在的線程中執行的mWorker#call方法
  • mWorkder#call調用了doInBackground,用戶經過實現這個抽象方法來進行耗時操做
  • mFuture#call執行完後調用mFuture#done

在上面的過程中,有兩個地方都調用了postResult,一個是mWorkder#call的最後,另外一個是mFuture#done,可是區別在於後者在調用以前會判斷mTaskInvokedfalse時纔會去執行,也就是在mWorkder#call沒有執行的狀況下,這是爲了不call方法沒有被執行時(提早取消了任務),postResult沒有被執行,致使使用者收不到任何回調。 postResult會經過InternalHandler把當前的AsyncTaskFutureTask的結果回調到主線程當中,以後調用finish方法,它會根據調用者是否執行過cancel方法來回調不一樣的函數:this

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

調用者經過重寫onProgressUpdate就能夠獲得當前的最新進度,AsyncTask最終會把結果回調到主線程當中:spa

protected final void publishProgress(Progress... values) {    
    if (!isCancelled()) {          
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();    
    }
}

private static class InternalHandler extends Handler {
    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、結論

通過上面的源碼分析,咱們有這麼幾個結論:線程

  • 靜態方法execute(Runnable runnable)AsyncTask其實沒什麼太大關係,只是用到了裏面一個靜態的線程池而已,AsyncTask內部的狀態都和它無關。
  • 當咱們對同一個AsyncTask實例調用execute(..)時,若是此時已經有任務正在執行,或者已經執行過任務,那麼會拋出異常。
  • onPreExecute()執行時,任務尚未被加入到隊列當中。
  • doInBackground是在子線程當中執行的,咱們調用cancel後,並不必定會當即獲得onCancel的回調,這是由於cancel只保證了mFuturedone方法被執行,有這麼幾種狀況:
  • 若是mWorkercall函數沒有執行,那麼這時mFuturedone方法被調用時,postResultIfNotInvoked是知足條件的,調用者能夠當即獲得onCancel回調。
  • 若是mWorkercall調用了,雖然mFuturedone執行了,可是它不知足條件(!mTaskInvoked.get()),那麼會一直等到doInBackground執行完全部的操做才經過return postResult返回,因此咱們須要在doInBackground中經過isCancel()來判斷是否須要提前返回,避免無用的等待。
  • postResult完畢以後, onCancelonPostExecute只會調用一個。
  • 任務是和AsyncTask實例綁定的,而若是AsyncTask又和Activity綁定了,若是在執行過程當中這個 AsyncTask實例被銷燬了(例如Activity被從新建立),那麼調用者在新的Activity中是沒法收到任何回調的,由於那已是另一個AsyncTask了。
  • 關於AsyncTask最大的問題實際上是內存泄漏,由於把它做爲Activity的內部類時,會默認持有Activity的引用,那麼這時候若是有任務正在執行,那麼 Activity 是沒法被銷燬的,這其實和Handler的泄漏相似,能夠有如下這麼一些用法:
  • 不讓它持有Activity的引用或者持有弱引用。
  • 保證在Activity在須要銷燬時cancel了全部的任務。
  • 使AsyncTask的執行與Activity的生命週期無關,能夠考慮經過創建一個沒有UIfragment來實現,由於在Activity重啓時,會自動保存有以前add進去的Fragment的實例,Fragment持有的仍是先前的 AsyncTask
  • 使用LoaderManager,把管理的活交給系統來執行。
相關文章
相關標籤/搜索