Android AsyncTask源碼解析产园,知其所以然

前言

AsyncTask是很多人接觸的第一個用來完成異步任務(wù)的東西。相信大家都知道它的使用方法夜郁,今天我們來看一下他的源碼什燕,來弄明白其中的原理。

源碼解析

首先先從入口開始竞端,execute方法會轉(zhuǎn)入executeOnExecutor方法屎即。

    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //sDefaultExecutor是一個SerialExecutor對象,關(guān)于他的介紹放在了文章末尾
        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;
    }

   

private volatile Status mStatus = Status.PENDING;
AsyncTask對象的初始狀態(tài)為PENDING,之后我們會介紹到技俐,當任務(wù)被取消乘陪,或者onPostExecute()執(zhí)行完之后,狀態(tài)會被設(shè)置為FINISHED虽另。
那么我們可以得到一個對開發(fā)有用的信息:
每個AsyncTask對象只能被execute一次暂刘,第二次被執(zhí)行時會拋出異常。
可以看到捂刺,我們經(jīng)常要重寫的onPreExecute()方法在這里被調(diào)用谣拣。之后就在線程池里執(zhí)行了這個任務(wù),那么這個mFuture是在哪初始化的呢族展?顯然只能是在構(gòu)造方法里森缠,我們?nèi)タ匆幌?/p>

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

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

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

這里對FutureTask做一個簡單的介紹,F(xiàn)utureTask實現(xiàn)了Runnable接口仪缸,所以可以在線程池中被執(zhí)行贵涵。在FutureTask的Run方法中,實際上被執(zhí)行的是傳入的Callable的call方法恰画,此處也就是指mWork的call方法宾茂,然后會執(zhí)行FutureTask的done方法。
經(jīng)過上面的介紹我們可以直到拴还,call和done方法都運行在線程池中跨晴,doInBackground方法在call中被執(zhí)行,所以doInBackground方法可以進行一些耗時操作片林,如網(wǎng)絡(luò)請求和數(shù)據(jù)庫操作端盆。

可以看到,doInBackground方法的返回值被傳入了postResult方法费封,跟蹤進來:

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

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

這里使用了Message焕妙!看到這里我想大家就應(yīng)該明白了,AsyncTask中是使用Handler來實現(xiàn)線程的切換的弓摘。代碼中是使用obtainMessage方法來獲取Message對象的焚鹊,這樣有什么好處呢?
obtainMessage()是從消息池中回收一個Message韧献,這樣比使用New Message()來創(chuàng)建Message對象更節(jié)省資源寺旺。
既然已經(jīng)使用sendToTarget方法將message發(fā)送,那么我們?nèi)ゲ榭磳?yīng)的handler:

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

當doInBackground方法執(zhí)行完之后势决,進入MESSAGE_POST_RESULT對應(yīng)的分支阻塑,那么MESSAGE_POST_PROGRESS呢?回想一下AsyncTask中需要在主線程中運行的代碼還有什么果复?
相信大家已經(jīng)想到了陈莽,AsyncTask中需要在主線程運行的方法有兩個:

  • onPostExecute(),獲取數(shù)據(jù)之后進行UI操作
  • onProgressUpdate(),在耗時操作的時候更新進度條

首先我們來看finish方法

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

如果這個任務(wù)已經(jīng)被取消了走搁,那么就不會調(diào)用onPostExecute()独柑,否則將會調(diào)用它。最后會將該AsyncTask對象的狀態(tài)設(shè)置為FINISHED私植。這樣一套流程就走完了~ 下面我們看一看關(guān)于更新進度條的部分忌栅。

我們知道,當在doInBackground()想要更新進度條的時候曲稼,應(yīng)該調(diào)用publishProgress()

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

然后我們再去看一看Handler索绪。

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

可以看到,onProgressUpdate方法會被調(diào)用贫悄。

下面我們來介紹默認的線程池瑞驱。

   /**
    *用來將任務(wù)排隊
    */
    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);
            }
        }
    }

mTask是一個隊列,相信大家也能看出來窄坦,SerialExecutor是用來將任務(wù)排隊的唤反,當某一個AsyncTask類的多個對象同時調(diào)用execute()時,任務(wù)會被順序執(zhí)行鸭津,我們寫個demo測試一下彤侍。

public class SecondActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TestAsyncTask testAsyncTask = new TestAsyncTask();
                testAsyncTask.execute();
            }
        });
    }

    private class TestAsyncTask extends AsyncTask<Object,Integer,String>{

        @Override
        protected String doInBackground(Object... params) {
            Log.d("test","doInBackground");
            try{
                Thread.sleep(3000);
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            Log.d("test","onPostExecute");
        }
    }
}

然后我連續(xù)快速地點擊3下button,打印結(jié)果如下:

05-10 14:12:11.623 1871-3036/com.gustiness.secondtrain D/test: doInBackground
05-10 14:12:11.623 1871-1871/com.gustiness.secondtrain D/test: onPostExecute
05-10 14:12:14.619 1871-3036/com.gustiness.secondtrain D/test: doInBackground
05-10 14:12:14.619 1871-1871/com.gustiness.secondtrain D/test: onPostExecute
05-10 14:12:17.619 1871-3036/com.gustiness.secondtrain D/test: doInBackground
05-10 14:12:17.619 1871-1871/com.gustiness.secondtrain D/test: onPostExecute

可以發(fā)現(xiàn)逆趋,當?shù)谝粋€任務(wù)執(zhí)行完之后拥刻,下一個任務(wù)才會被執(zhí)行,這也就驗證了我們剛才的結(jié)論父泳。

我們稍微修改一下代碼,驗證文章開頭出的結(jié)論:每個AsyncTask對象只能被execute一次吴汪,第二次被執(zhí)行時會拋出異常惠窄。

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Button button = (Button) findViewById(R.id.button);
       final TestAsyncTask testAsyncTask = new TestAsyncTask();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testAsyncTask.execute();
            }
        });
    }

連續(xù)點擊兩次button,程序崩潰漾橙,報錯信息為:

05-10 14:18:19.495 8499-8499/com.gustiness.secondtrain E/AndroidRuntime: FATAL EXCEPTION: main
                                                                         java.lang.IllegalStateException: Cannot execute task: the task is already running.
                                                                             at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:575)
                                                                             at android.os.AsyncTask.execute(AsyncTask.java:534)
                                                                             at com.gustiness.secondtrain.SecondActivity$1.onClick(SecondActivity.java:25)

等AsyncTask執(zhí)行完成之后再點擊button杆融,程序崩潰,報錯信息為:

05-10 14:21:06.171 10645-10645/com.gustiness.secondtrain E/AndroidRuntime: FATAL EXCEPTION: main
                                                                           java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
                                                                               at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:578)
                                                                               at android.os.AsyncTask.execute(AsyncTask.java:534)
                                                                               at com.gustiness.secondtrain.SecondActivity$1.onClick(SecondActivity.java:25)

驗證了文章開頭處的結(jié)論:每個AsyncTask對象只能被執(zhí)行一次霜运。

到這里我們也就將AsyncTask的源碼分析完了脾歇,是不是覺得理解的更深入一些了呢?平時養(yǎng)成看源碼的習慣淘捡,會成長的更快藕各,一起加油~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市焦除,隨后出現(xiàn)的幾起案子激况,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乌逐,死亡現(xiàn)場離奇詭異竭讳,居然都是意外死亡,警方通過查閱死者的電腦和手機浙踢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門绢慢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洛波,你說我怎么就攤上這事胰舆。” “怎么了奋岁?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵思瘟,是天一觀的道長。 經(jīng)常有香客問我闻伶,道長滨攻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任蓝翰,我火速辦了婚禮光绕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畜份。我一直安慰自己诞帐,他們只是感情好,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布爆雹。 她就那樣靜靜地躺著停蕉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钙态。 梳的紋絲不亂的頭發(fā)上慧起,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音册倒,去河邊找鬼蚓挤。 笑死,一個胖子當著我的面吹牛驻子,可吹牛的內(nèi)容都是我干的灿意。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼崇呵,長吁一口氣:“原來是場噩夢啊……” “哼缤剧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起域慷,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鞭执,失蹤者是張志新(化名)和其女友劉穎司顿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兄纺,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡大溜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了估脆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钦奋。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖疙赠,靈堂內(nèi)的尸體忽然破棺而出付材,到底是詐尸還是另有隱情,我是刑警寧澤圃阳,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布厌衔,位于F島的核電站,受9級特大地震影響捍岳,放射性物質(zhì)發(fā)生泄漏富寿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一锣夹、第九天 我趴在偏房一處隱蔽的房頂上張望页徐。 院中可真熱鬧,春花似錦银萍、人聲如沸变勇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搀绣。三九已至,卻和暖如春戳气,著一層夾襖步出監(jiān)牢的瞬間链患,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工物咳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹄皱。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓览闰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親巷折。 傳聞我的和親對象是個殘疾皇子压鉴,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容