源碼解析--AsyncTask

1.概念

相信你們對AsyncTask都不陌生,對於執行耗時任務,而後更新UI是一把利器,固然也是替代Thread + Handler 的一種方式,若是你對Handler機制還不瞭解請看這篇文章:java

https://my.oschina.net/quguangle/blog/792219android

2.案例

public class MainActivity extends Activity  
{  
  
    private static final String TAG = "MainActivity";  
    private ProgressDialog mDialog;  
    private TextView mTextView;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        mTextView = (TextView) findViewById(R.id.id_tv);  
  
        mDialog = new ProgressDialog(this);  
        mDialog.setMax(100);  
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
        mDialog.setCancelable(false);  
  
        new MyAsyncTask().execute();  
  
    }  
  
    private class MyAsyncTask extends AsyncTask<Void, Integer, Void>  
    {  
  
        @Override  
        protected void onPreExecute()  
        {  
            mDialog.show();  
            Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");  
        }  
  
        @Override  
        protected Void doInBackground(Void... params)  
        {  
  
            // 模擬數據的加載,耗時的任務  
            for (int i = 0; i < 100; i++)  
            {  
                try  
                {  
                    Thread.sleep(80);  
                } catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
                publishProgress(i);  
            }  
  
            Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");  
            return null;  
        }  
  
        @Override  
        protected void onProgressUpdate(Integer... values)  
        {  
            mDialog.setProgress(values[0]);  
            Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");  
        }  
  
        @Override  
        protected void onPostExecute(Void result)  
        {  
            // 進行數據加載完成後的UI操做  
            mDialog.dismiss();  
            mTextView.setText("LOAD DATA SUCCESS ");  
            Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");  
        }  
    }  
}

代碼的解釋:面試

進入某個Activity,Activity中須要的數據來自於網絡或者其它耗時操做,能夠在AsyncTask中onPreExecute完成一些準備操做,好比上例中顯示進度對話框;而後在doInBackground完成耗時操做,在進行耗時操做時還能不時的經過publishProgress給onProgressUpdate中傳遞參數,而後在onProgressUpdate中能夠進行UI操做,好比上例更新進度條的進度;當耗時任務執行完成後,最後在onPostExecute進行設置控件數據更新UI等操做,例如隱藏進度對話框。網絡

三、源碼解析

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

對應源碼的解釋:多線程

1.設置當前AsyncTask的狀態爲RUNNING,上面的switch也能夠看出,每一個異步任務在完成前只能執行一次。
2.執行了onPreExecute(),當前依然在UI線程,因此咱們能夠在其中作一些準備工做。
將咱們傳入的參數賦值給了mWorker.mParams
3.exec.execute(mFuture)併發

相信你們對出現的mWorker,以及出現的mFuture都會有些困惑。
mWorker找到這個類:app

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

對應源碼的解釋:異步

能夠看到是Callable的子類,且包含一個mParams用於保存咱們傳入的參數,下面看初始化mWorker的代碼:async

public AsyncTask() {  
        mWorker = new WorkerRunnable<Params, Result>() {  
            public Result call() throws Exception {  
                mTaskInvoked.set(true);  
  
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
                //noinspection unchecked  
                return postResult(doInBackground(mParams));  
            }  
        };  
//….  
          
}

能夠看到mWorker在構造方法中完成了初始化,而且由於是一個抽象類,在這裏new了一個實現類,實現了call方法,call方法中設置mTaskInvoked=true,且最終調用doInBackground(mParams)方法,並返回Result值做爲參數給postResult方法.能夠看到咱們的doInBackground出現了,下面繼續看:ide

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

對應源碼的解釋:

能夠看到postResult中出現了咱們熟悉的異步消息機制,傳遞了一個消息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就是一個簡單的攜帶參數的對象。

看到這,我相信你們確定會想到,在某處確定存在一個sHandler,且複寫了其handleMessage方法等待消息的傳入,以及消息的處理。

private static final InternalHandler sHandler = new InternalHandler();  
    private static class InternalHandler extends Handler {  
        @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;  
            }  
        }  
}

對應源碼的解釋:

哈哈,出現了咱們的handleMessage,能夠看到,在接收到MESSAGE_POST_RESULT消息時,執行了result.mTask.finish(result.mData[0]);其實就是咱們的AsyncTask.this.finish(result),

因而看finish方法

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

對應源碼的解釋:

能夠看到,若是咱們調用了cancel()則執行onCancelled回調;正常執行的狀況下調用咱們的onPostExecute(result);主要這裏的調用是在handler的handleMessage中,因此是在UI線程中。最後將狀態置爲FINISHED。

mWoker看完了,應該到咱們的mFuture了,依然實在構造方法中完成mFuture的初始化,將mWorker做爲參數,複寫了其done方法。

public AsyncTask() {  
    ...  
        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 occured while executing doInBackground()",  
                            e.getCause());  
                } catch (CancellationException e) {  
                    postResultIfNotInvoked(null);  
                }  
            }  
        };  
}

對應源碼的解釋:

任務執行結束會調用:postResultIfNotInvoked(get());get()表示獲取mWorker的call的返回值,即Result.而後看postResultIfNotInvoked方法

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

對應源碼的解釋:

若是mTaskInvoked不爲true,則執行postResult;可是在mWorker初始化時就已經將mTaskInvoked爲true,因此通常這個postResult執行不到。
好了,到了這裏,已經介紹完了execute方法中出現了mWorker和mFurture,不過這裏一直是初始化這兩個對象的代碼,並無真正的執行。下面咱們看真正調用執行的地方。
execute方法中的:
還記得上面的提到的:exec.execute(mFuture)

exec爲executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看這個sDefaultExecutor

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

對應源碼的解釋:

1.能夠看到sDefaultExecutor其實爲SerialExecutor的一個實例,其內部維持一個任務隊列;直接看其execute(Runnable runnable)方法,將runnable放入mTasks隊尾;
2.判斷當前mActive是否爲空,爲空則調用scheduleNext方法
3.scheduleNext,則直接取出任務隊列中的隊首任務,若是不爲null則傳入THREAD_POOL_EXECUTOR進行執行。

下面看THREAD_POOL_EXECUTOR爲什麼方神聖:

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

能夠看到就是一個本身設置參數的線程池,參數爲:

private static final int CORE_POOL_SIZE = 5;  
private static final int MAXIMUM_POOL_SIZE = 128;  
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>(10);

對應源碼的解釋:

看到這裏,你們可能會認爲,背後原來有一個線程池,且最大支持128的線程併發,加上長度爲10的阻塞隊列,可能會以爲就是在快速調用138個之內的AsyncTask子類的execute方法不會出現問題,而大於138則會拋出異常。
其實不是這樣的,咱們再仔細看一下代碼,回顧一下sDefaultExecutor,真正在execute()中調用的爲sDefaultExecutor.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();  
                    }  
                }  
            });  
            if (mActive == null) {  
                scheduleNext();  
            }  
        }  
        protected synchronized void scheduleNext() {  
            if ((mActive = mTasks.poll()) != null) {  
                THREAD_POOL_EXECUTOR.execute(mActive);  
            }  
        }  
}

對應源碼的解釋:

能夠看到,若是此時有10個任務同時調用execute(s synchronized)方法,第一個任務入隊,而後在mActive = mTasks.poll()) != null被取出,而且賦值給mActivte,而後交給線程池去執行。而後第二個任務入隊,可是此時mActive並不爲null,並不會執行scheduleNext();因此若是第一個任務比較慢,10個任務都會進入隊列等待;真正執行下一個任務的時機是,線程池執行完成第一個任務之後,調用Runnable中的finally代碼塊中的scheduleNext,因此雖然內部有一個線程池,其實調用的過程仍是線性的。一個接着一個的執行,至關於單

四、總結

到此源碼解釋完畢,因爲代碼跨度比較大,咱們再回顧一下:

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

 

對應的源碼解釋:

1.設置當前AsyncTask的狀態爲RUNNING,上面的switch也能夠看出,每一個異步任務在完成前只能執行一次。
2.執行了onPreExecute(),當前依然在UI線程,因此咱們能夠在其中作一些準備工做。
3.將咱們傳入的參數賦值給了mWorker.mParams ,mWorker爲一個Callable的子類,且在內部的call()方法中,調用了doInBackground(mParams),而後獲得的返回值做爲postResult的參數進行執行;postResult中經過sHandler發送消息,最終sHandler的handleMessage中完成onPostExecute的調用。
4.exec.execute(mFuture),mFuture爲真正的執行任務的單元,將mWorker進行封裝,而後由sDefaultExecutor交給線程池進行執行。

五、publishProgress

說了這麼多,咱們好像忘了一個方法:publishProgress

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

也很簡單,直接使用sHandler發送一個消息,而且攜帶咱們傳入的值;

private static class InternalHandler extends Handler {  
        @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;  
            }  
        }  
}

在handleMessage中進行了咱們的onProgressUpdate(result.mData);的調用。

六、AsyncTask曾經缺陷

記得之前有個面試題常常會問道:AsyncTask運行的原理是什麼?有什麼缺陷?

之前對於缺陷的答案多是:AsyncTask在併發執行多個任務時發生異常。其實仍是存在的,在3.0之前的系統中仍是會以支持多線程併發的方式執行,支持併發數也是咱們上面所計算的128,阻塞隊列能夠存放10個;也就是同時執行138個任務是沒有問題的;而超過138會立刻出現Java.util.concurrent.RejectedExecutionException;

而在在3.0以上包括3.0的系統中會爲單線程執行(即咱們上面代碼的分析);

空說無憑:下面看測試代碼:

package com.example.zhy_asynctask_demo01;  
  
import android.app.Activity;  
import android.app.ProgressDialog;  
import android.os.AsyncTask;  
import android.os.Bundle;  
import android.util.Log;  
import android.widget.TextView;  
  
public class MainActivity extends Activity  
{  
  
    private static final String TAG = "MainActivity";  
    private ProgressDialog mDialog;  
    private TextView mTextView;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        mTextView = (TextView) findViewById(R.id.id_tv);  
  
        mDialog = new ProgressDialog(this);  
        mDialog.setMax(100);  
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
        mDialog.setCancelable(false);  
          
          
        for(int i = 1 ;i <= 138 ; i++)  
        {  
            new MyAsyncTask2().execute();  
        }  
          
        //new MyAsyncTask().execute();  
  
          
    }  
  
    private class MyAsyncTask2 extends AsyncTask<Void,Void, Void>  
    {  
  
        @Override  
        protected Void doInBackground(Void... params)  
        {  
            try  
            {  
                Log.e(TAG, Thread.currentThread().getName());  
                Thread.sleep(10000);  
            } catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
            return null;  
        }  
          
    }  
}

能夠看到我for循環中執行138個異步任務,每一個異步任務的執行須要10s;下面使用2.2的模擬器進行測試:

輸出結果爲:

AsyncTask#1 - AsyncTask #128同時輸出
而後10s後,另外10個任務輸出。
能夠分析結果,獲得結論:AsyncTask在2.2的系統中同時支持128個任務併發,至少支持10個任務等待;

下面將138個任務,改爲139個任務:

for(int i = 1 ;i <= 139 ; i++)  
{  
    new MyAsyncTask2().execute();  
}

運行結果:會發生異常:java.util.concurrent.RejectedExecutionException ; 因而能夠肯定僅支持10個任務等待,超過10個則當即發生異常。
簡單說一下出現異常的緣由:如今是139個任務,幾乎同時提交,線程池支持128個的併發,而後阻塞隊列數量爲10,此時當第11個任務提交的時候則會發生異常。

簡單看一下源碼:

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

看ThreadPoolExecutor的execute方法:

if (isRunning(c) && workQueue.offer(command)) {  
            int recheck = ctl.get();  
            if (! isRunning(recheck) && remove(command))  
                reject(command);  
            else if (workerCountOf(recheck) == 0)  
                addWorker(null, false);  
        }  
        else if (!addWorker(command, false))  
            reject(command);

當阻塞隊列滿的時候workQueue.offer(command)返回false;而後執行addWorker(command,false)方法,若是返回false則執行reject()方

private boolean addWorker(Runnable firstTask, boolean core) {  
…  
int wc = workerCountOf(c);  
                if (wc >= CAPACITY ||  
                    wc >= (core ? corePoolSize : maximumPoolSize))  
                    return false;  
…  
}

能夠看到當任務數目大於容量則返回false,最終在reject()中拋出異常。

上面就是使用2.2模擬器測試的結果;

下面將系統改成4.1.1,也就是個人測試機小米2s

把線程數改成139甚至1000,你能夠看到任務一個接一個的在那緩慢的執行,不會拋什麼異常,不過線程卻是1個1個的在那執行;

好了,若是如今你們去面試,被問到AsyncTask的缺陷,能夠分爲兩個部分說,在3.0之前,最大支持128個線程的併發,10個任務的等待。在3.0之後,不管有多少任務,都會在其內部單線程執行。

相關文章
相關標籤/搜索