Android面試系列之AsyncTask源碼分析

在Android開(kāi)發(fā)道路上,有一個(gè)類你是無(wú)論如何都無(wú)法繞過(guò)去的。那就是AsyncTask,因?yàn)槭褂玫淖銐蚝?jiǎn)單,在面試中多線程很頻繁聊到這個(gè)。

AsyncTask的使用起始很簡(jiǎn)單,在源碼中類為抽象類 我們只需要復(fù)寫doInBackground方法即可完成子線程的業(yè)務(wù)編寫。我們從AsyncTask中的方法 onPreExecute()doInBackground() onPostExecute()onProgressUpdate()來(lái)入手分析AsyncTask的源碼

AsyncTask的對(duì)象一般是執(zhí)行execute()方法來(lái)啟動(dòng),我們來(lái)看一下

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

接著看executeOnExecutor

@MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //此處省略一些
        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = params;
        exec.execute(mFuture);
        return this;
    }

在方法中首先調(diào)用了onPreExecute()方法執(zhí)行在主線程中,然后講參數(shù)賦給了Worker對(duì)象,在后面我們會(huì)知道這是線程執(zhí)行單位。然后調(diào)用了線程調(diào)度器來(lái)執(zhí)行Future,這個(gè)后面會(huì)解釋到。

然后我們?cè)賮?lái)看一下線程池的調(diào)用情況

 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) {
                    // 真正線程池調(diào)用
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

這塊代碼 首先講要執(zhí)行的代碼放到Tasks隊(duì)列中,然后判斷是否有任務(wù)執(zhí)行 如果線程池空閑則調(diào)用scheduleNext();方法 這個(gè)方法中完成任務(wù)的執(zhí)行

THREAD_POOL_EXECUTOR的初始化如下

 public static final Executor THREAD_POOL_EXECUTOR;
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

可能你看到這里已經(jīng)有點(diǎn)頭暈了,但是沒(méi)關(guān)系,此處我們放慢腳步來(lái)解釋下幾個(gè)名詞

  • THREAD_POOL_EXECUTOR
    這個(gè)是用于任務(wù)執(zhí)行的線程池 ,所有的任務(wù)都是在這個(gè)線程池中完成調(diào)用
  • SerialExecutor
    這個(gè)線程池用于用于任務(wù)的排隊(duì),默認(rèn)實(shí)現(xiàn)也能看出來(lái),它是串行執(zhí)行任務(wù)。
  • FutureTask
    FutureTask一個(gè)可取消的異步計(jì)算, 使用子線程計(jì)算返回的結(jié)果Result作為泛型,并且將WorkerRunnable作為構(gòu)造參數(shù)傳入
  • WorkerRunnable
    真正的子線程執(zhí)行單位
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;
            }
        };

首先設(shè)置 mTaskInvoked.set(true);標(biāo)識(shí)任務(wù)已經(jīng)執(zhí)行,然后調(diào)整線程的優(yōu)先級(jí),接著調(diào)用doInBackground(mParams); 因?yàn)閃orkerRunnable的call()方法會(huì)被FutureTask的run方法調(diào)用,所以doInBackground()方法最后執(zhí)行在子線程中。postResult(result);最后將結(jié)果返回到UI線程。

我們都知道Handler的作用很大程度在于線程間傳輸,我們首先看一下 postResult(result)方法

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

很熟悉的代碼 通過(guò)Handler將MESSAGE_POST_RESULT發(fā)送到目標(biāo)來(lái)處理,我們首先看一下AsyncTask類中的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;
            }
        }
    }

直接很簡(jiǎn)單的在構(gòu)造函數(shù)上Looper.getMainLooper() 獲取UI線程的Looper
看到switch語(yǔ)句中有兩種類型 1. MESSAGE_POST_RESULT: 2. MESSAGE_POST_PROGRESS 這我們?cè)谄綍r(shí)的使用中再熟悉不過(guò)了 一個(gè)用來(lái)在UI 線程中獲取結(jié)果的 onPostExecute(),另外一個(gè)則是onProgressUpdate 用來(lái)刷新任務(wù)完成的進(jìn)度的。

接下來(lái)我們先看一下AsyncTask的finish方法

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

簡(jiǎn)單判斷下是否取消isCancelled(),如果取消則調(diào)用onCancelled(result);,否則將結(jié)果傳遞到onPostExecute(result);在這里我們可以得出一個(gè)結(jié)論,AsyncTask為什么不能中斷線程,在AsyncTask的方法中Cancel方法的調(diào)用只是在線程執(zhí)行完需要返回結(jié)果的時(shí)候做的一個(gè)標(biāo)記符而已。

PS 這里多說(shuō)幾句 Java最開(kāi)始的時(shí)候是沒(méi)有正式中斷線程的辦法,一般都是調(diào)用interrupt()來(lái)強(qiáng)制中斷,然后在Run()方法中來(lái)捕獲這個(gè)異常。我們?cè)谄綍r(shí)的開(kāi)發(fā)過(guò)程中一般使用Run方法中循環(huán) 然后使用一個(gè)volatieboolean變量來(lái)提前終止掉Run方法來(lái)達(dá)到中斷線程的目的。

到這里 我們已經(jīng)基本完成了AsyncTask的源碼分析了,因?yàn)锳syncTask的源碼是只有500行,所以看下來(lái)起始很輕松,如果有些類的代碼達(dá)到了一萬(wàn)多行的時(shí)候,我們應(yīng)該用巧方法來(lái)閱讀源碼,最常見(jiàn)的方法便是從使用的角度來(lái)反向查看代碼如何實(shí)現(xiàn)的,去粗取精 了解到整個(gè)類的是如何設(shè)計(jì)的就行,做到看完后心中已經(jīng)有了框架模樣就可以了,沒(méi)必要一行一行看下去也不現(xiàn)實(shí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,577評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 178,486評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,852評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,600評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,944評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 43,108評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,652評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,385評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,616評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,798評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 35,205評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,537評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,334評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,570評(píng)論 2 379

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

  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 3,251評(píng)論 1 15
  • Android開(kāi)發(fā)者:你真的會(huì)用AsyncTask嗎? 導(dǎo)讀.1 在Android應(yīng)用開(kāi)發(fā)中,我們需要時(shí)刻注意保證...
    cxm11閱讀 2,728評(píng)論 0 29
  • 由于Android的特性,如果要執(zhí)行耗時(shí)操作,則必須方法子線程中執(zhí)行。除了Thread可以開(kāi)啟子線程外,Andro...
    Ruheng閱讀 25,856評(píng)論 6 18
  • AsyncTask AsyncTask是android再API-3中加入的類,為了方便開(kāi)發(fā)人員執(zhí)行后臺(tái)操作并在UI...
    Mr韶先生閱讀 333評(píng)論 0 1
  • 文/熠歆 那年,2014年6月畢業(yè)季,我有想過(guò)去貴州考試,而且欲望也強(qiáng),特意買了教師招聘的書,還有準(zhǔn)備過(guò),只是最后...
    熠歆閱讀 184評(píng)論 4 2