Android AsyncTask 源碼解析

標(biāo)簽:Android AsyncTask 源碼解析


1.關(guān)于AsyncTask

1.1 什么是AsyncTask?

根據(jù)Google的官方文檔

This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask允許我們執(zhí)行后臺操作并在UI線程中更新結(jié)果,而不用去操作threads或handlers。這句解釋很明顯的告訴了我們:AsyncTask是一個(gè)thread和handler的封裝疟呐。

1.2 為什么使用AsyncTask?

既然AsyncTask只是一個(gè)封裝,那它到底幫我們解決了哪些細(xì)節(jié)上的問題呢唐全?除了提供更簡明的接口之外,當(dāng)我們單純的使用thread+handler的組合時(shí)蕊玷,其實(shí)我們忽略了線程所帶來的一系列問題邮利,比如創(chuàng)建線程的開銷、如何管理線程等等垃帅,特別是當(dāng)多任務(wù)情況下延届,情況會更加復(fù)雜。

2.源碼解析

*注:源碼基于api-24
源碼點(diǎn)進(jìn)去一大堆贸诚,我們這里直接從任務(wù)被執(zhí)行開始看起方庭。同時(shí)為了避免深入細(xì)節(jié)無法自拔的情況,我先大體看一下整個(gè)的調(diào)用順序酱固,而不去太糾結(jié)具體實(shí)現(xiàn)械念。

@MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

可以看到子類直接調(diào)用了父類的execute()方法。該方法又調(diào)用了executeOnExecutor() 方法运悲。同時(shí)將sDefaultExecutor這個(gè)靜態(tài)變量傳遞給了executeOnExecutor方法龄减。那這個(gè)sDefaultExecutor又是什么鬼呢?

   public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
   //省略...
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

原來sDefaultExecutor只是一個(gè)Executor接口班眯,而它的實(shí)現(xiàn)則是SerialExecutor這樣一個(gè)內(nèi)部類希停。我們先把這個(gè)類的具體實(shí)現(xiàn)放下烁巫,不過從類的名字就可以猜出個(gè)大概來,這是一個(gè)用來順序執(zhí)行任務(wù)的線程池類宠能。OK這個(gè)坑我們先邁過去亚隙,回頭再來繼續(xù)填它。再看看executeOnExecutor()方法:

  @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
            //判斷當(dāng)前任務(wù)狀態(tài)
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                //任務(wù)運(yùn)行中
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                //任務(wù)結(jié)束
                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行先判斷當(dāng)mStatus的值违崇,即前任務(wù)的狀態(tài)阿弃,只有沒有運(yùn)的任務(wù)才能夠被運(yùn)行,否則將會拋出異常亦歉。再往下看恤浪,在將mStatus的值置為RUNNING之后,我看到了一個(gè)十分熟悉的方法onPreExecute()肴楷,而這個(gè)方法是個(gè)空實(shí)現(xiàn),具體在執(zhí)行任務(wù)之前要做些什么荠呐,是由我們自己去實(shí)現(xiàn)的赛蔫。23-24行突然冒出兩個(gè)新變量,mWorker和mFuture泥张,回到變量聲明和構(gòu)造函數(shù)處看看他們的實(shí)質(zhì)呵恢。

 //變量聲明
 private final WorkerRunnable<Params, Result> mWorker;
 private final FutureTask<Result> mFuture;
 
 ...
 
 //構(gòu)造函數(shù),變量初始化
 public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //...省略部分代碼
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
              //...省略部分代碼
        };
    }

可以看到mWorker為一個(gè)WorkRunnale類媚创,該類為一個(gè)靜態(tài)抽象內(nèi)部類渗钉,包含了一個(gè)初始化參數(shù)Params[],并繼承了Callable接口(待實(shí)現(xiàn))钞钙。

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

而mFuture為FutureTask的一個(gè)實(shí)例鳄橘,F(xiàn)utureTask是Java并發(fā)編程中一個(gè)十分常見的類,主要用來異步執(zhí)行任務(wù)芒炼,并返回結(jié)果瘫怜。很顯然,這些特點(diǎn)都是我們需要本刽,但單純的Thread所不具備的鲸湃。FutureTask需要一個(gè)Callable接口來進(jìn)行初始化,而這里我們使用了mWorker這個(gè)變量子寓。
介紹了這么多暗挑,我們再捋一下思路:我們自己實(shí)現(xiàn)多XXXTask執(zhí)行了execute()方法,再調(diào)用了我們重寫多onPreExecute()方法后斜友,該方法最終把一個(gè)任務(wù)(mFuture)交給了一個(gè)串行執(zhí)行任務(wù)的線程池(sDefaultExecutor)去執(zhí)行炸裆。那么剩下的問題就只有以下幾個(gè)了:
1.任務(wù)(mFuture)具體做了些什么?
2.任務(wù)(mFuture)執(zhí)行完后蝙寨,如何通知UI線程的晒衩?
再回頭看看mWork中Callable的實(shí)現(xiàn):

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

在這里我們發(fā)現(xiàn)一個(gè)十分熟悉的方法——doInBackground()嗤瞎,而該方法的簽名是protected abstract Result doInBackground(Params... params)。是我們在子類中必須去實(shí)現(xiàn)的听系,因此mFuture具體做的事情就是我們在doInBackground()中的具體實(shí)現(xiàn)贝奇。再看看最后一行postResult()(此時(shí)任務(wù)有可能并未完成)

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

再看看任務(wù)執(zhí)行結(jié)束后,mFuture中done()方法的具體實(shí)現(xiàn):

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

可見靠胜,任務(wù)結(jié)束后同樣也調(diào)用了postResult()方法掉瞳。而該方法又是如何將結(jié)果傳遞給UI線程的呢?答案也很簡單——通過Handler浪漠,這里的getHandler()方法返回一個(gè)sHandler靜態(tài)成員變量陕习,其實(shí)現(xiàn)如下:

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

可以看到,構(gòu)造器通過Looper.getMainLooper()來初始化了Hanlder址愿,這也就是為什么hanlderMessage的具體執(zhí)行是在UI線程(主線程)中的原因该镣。最后判斷消息的種類追驴,如果為MESSAGE_POST_PROGRESS就調(diào)用我們自己實(shí)現(xiàn)的onProgress方法沙绝;如果為MESSAGE_POST_RESULT就調(diào)用finish方法,而finish()方法又會判斷當(dāng)前任務(wù)是否執(zhí)行完成亲茅。若執(zhí)行完成就交給我們實(shí)現(xiàn)的onPostExecute()方法娘纷,若還未執(zhí)行完成就就給我們實(shí)現(xiàn)的onCancelled()方法嫁审。這樣一來整個(gè)AsyncTask的大致流程我們就走完了~
再次整理一下流程:
1.我們自己實(shí)現(xiàn)的xxxTask(繼承AsyncTask)執(zhí)行了execute()方法;
2.execute方法調(diào)用了executeOnExecutor()方法赖晶,為我們的異步任務(wù)指定了一個(gè)串行執(zhí)行任務(wù)的線程池律适;
3.該線程池調(diào)用了mFuture的call()方法,在子線程執(zhí)行了我們重寫的doInBackground()方法遏插,最終發(fā)送一個(gè)消息給handler(用主線程的looper初始化);
4.該handler根據(jù)消息類型的不同捂贿,執(zhí)行結(jié)束任務(wù)onPostExecute()或更新進(jìn)度的操作onProgressUpdate();

到此涩堤,我們常用的AsyncTask的方法眷蜓,是不是不用再去翻文檔我們也能寫出先后順序了呢,成就感滿滿噠~

3.細(xì)節(jié)問題

---待更新---

Todo-List
  • 細(xì)節(jié)問題
    - [x] 線程池相關(guān)
    - [x] 線程池順序執(zhí)行任務(wù)的實(shí)現(xiàn)
    - [x] 部分細(xì)節(jié)代碼
    - [x] WorkerRunnable 和 AsyncTaskResult
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胎围,一起剝皮案震驚了整個(gè)濱河市吁系,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌白魂,老刑警劉巖汽纤,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異福荸,居然都是意外死亡蕴坪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來背传,“玉大人呆瞻,你說我怎么就攤上這事【毒粒” “怎么了痴脾?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長梳星。 經(jīng)常有香客問我赞赖,道長,這世上最難降的妖魔是什么冤灾? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任前域,我火速辦了婚禮,結(jié)果婚禮上韵吨,老公的妹妹穿的比我還像新娘匿垄。我一直安慰自己,他們只是感情好学赛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布年堆。 她就那樣靜靜地躺著,像睡著了一般盏浇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芽狗,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天绢掰,我揣著相機(jī)與錄音,去河邊找鬼童擎。 笑死滴劲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的顾复。 我是一名探鬼主播班挖,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芯砸!你這毒婦竟也來了萧芙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤假丧,失蹤者是張志新(化名)和其女友劉穎双揪,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體包帚,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渔期,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疯趟。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拘哨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出信峻,到底是詐尸還是另有隱情倦青,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布站欺,位于F島的核電站姨夹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏矾策。R本人自食惡果不足惜磷账,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贾虽。 院中可真熱鬧逃糟,春花似錦、人聲如沸蓬豁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽地粪。三九已至取募,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蟆技,已是汗流浹背玩敏。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留质礼,地道東北人旺聚。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像眶蕉,于是被迫代替她去往敵國和親砰粹。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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