從0開始學(xué)安卓源碼分析--AsyncTask源碼

問題:

  1. 為什么一個(gè)AsyncTask只能被執(zhí)行一次?
  2. 為什么AsyncTask是串行執(zhí)行的, 不能并行嗎?
  3. 為什么AsyncTask必須在UI線程中創(chuàng)建?

帶著問題看源碼, 穩(wěn).

看一個(gè)源碼當(dāng)然從其構(gòu)造函數(shù)出發(fā).

public AsyncTask(@Nullable Looper callbackLooper) {
        // 這里是為什么需要在主線程創(chuàng)建的原因, 不然怎么去更新ui?
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // WorkerRunnable實(shí)現(xiàn)callable接口并實(shí)現(xiàn)了它的call()方法
        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);
                }
            }
        };
    }

在這個(gè)構(gòu)造方法里可以看到doInBackground(mParams) 方法, 也就是我們繼承AsyncTask 復(fù)寫的方法. 該方法返回了result結(jié)果, 最終在finally里調(diào)用postResult(result); 將結(jié)果通過handler傳遞進(jìn)行處理. 在handler的handleMessage中會調(diào)用AsyncTask的finish()方法.

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

這里就找到了 onPostExecute(result); 將結(jié)果通過onPostExecute回調(diào)到上層了.

FutureTask是一個(gè)可管理的任務(wù)類, 它實(shí)現(xiàn)了Runnable和Callable兩個(gè)接口, 稍微瞄一眼源碼可知其可以對Runnable和Callable進(jìn)行包裝.
這里將WorkerRunnable作為入?yún)鬟f給了FutureTask.
AsyncTask還并未開始執(zhí)行, 真正執(zhí)行需要調(diào)用AsyncTask.execute(params);, 并將params傳遞進(jìn)去.

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

接著看executeOnExecutor(sDefaultExecutor, params);

這里需要看下AsyncTask的三個(gè)狀態(tài):

  • PENDING // 等待, 表示任務(wù)未執(zhí)行
  • RUNNING // 表示任務(wù)正在運(yùn)行
  • FINISHED // 表示任務(wù)已經(jīng)完成結(jié)束
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        // 這里也是為什么一個(gè)AsyncTask只能執(zhí)行一次的原因
        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)");
            }
        }
        // 更改為運(yùn)行態(tài)
        mStatus = Status.RUNNING;
        // 執(zhí)行任務(wù)前調(diào)用onPreExecute()方法, 我們會在onPreExecute進(jìn)行一些不耗時(shí)初始化的操作
        onPreExecute();

        mWorker.mParams = params;
        // 這里開始真正執(zhí)行task任務(wù)
        exec.execute(mFuture);

        return this;
    }

調(diào)用exec.execute(mFuture);真正執(zhí)行任務(wù)的exec是什么呢?
是傳遞進(jìn)來的sDefaultExecutor .

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
        // ArrayDeque 先進(jìn)先出的雙端隊(duì)列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            // 先將任務(wù)都存入隊(duì)列里
            // 當(dāng)前沒有執(zhí)行的任務(wù)則從隊(duì)列中取出一個(gè)用THREAD_POOL_EXECUTOR執(zhí)行.
            // 當(dāng)前有執(zhí)行的任務(wù)則是在任務(wù)完成后的finally里取出一個(gè)執(zhí)行
            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);
            }
        }
    }

sDefaultExecutor是一個(gè)靜態(tài)的串行執(zhí)行器, 也就是如果都調(diào)用AsyncTask.execute(params);方法, 那么一個(gè)進(jìn)程內(nèi)所有AsyncTask都是由sDefaultExecutor 串行執(zhí)行的. 這樣保證一個(gè)時(shí)間只有一個(gè)任務(wù)執(zhí)行.
如果需要并行執(zhí)行任務(wù)則需要調(diào)用executeOnExecutor(Executor exec,Params... params)

另外注意再看看THREAD_POOL_EXECUTOR

private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
            
 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;
    }

線程池用到了一個(gè)容量128的阻塞隊(duì)列.

到這里我們繼承AsyncTask需要復(fù)寫的方法,就都已經(jīng)在流程中出現(xiàn)了

  • onPreExecute()
  • doInBackground(params)
  • onPostExecute(result)

還有另外一個(gè)onProgressUpdate(progress) 在哪兒呢?
搜索一下源碼.

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

可以在doInBackground()中調(diào)用publishProgress()方法來進(jìn)行進(jìn)度的更新. 通過handler來將更新進(jìn)度發(fā)布到UI線程并回調(diào)onProgressUpdate(progress)方法

AsyncTask的源碼到這里已經(jīng)分析完了, 總的來說難度不大. 最后AsyncTask可能會導(dǎo)致內(nèi)存泄漏, 記得適時(shí)的調(diào)用AsyncTask的cancel()方法

理解記憶關(guān)鍵詞:
handler誓沸、FutureTask(Runnable)合武、SerialExecutor線程池芹敌、主線程初始化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慢洋,一起剝皮案震驚了整個(gè)濱河市伸头,隨后出現(xiàn)的幾起案子匾效,更是在濱河造成了極大的恐慌,老刑警劉巖恤磷,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件面哼,死亡現(xiàn)場離奇詭異,居然都是意外死亡扫步,警方通過查閱死者的電腦和手機(jī)魔策,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來河胎,“玉大人闯袒,你說我怎么就攤上這事。” “怎么了政敢?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵其徙,是天一觀的道長。 經(jīng)常有香客問我喷户,道長唾那,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任褪尝,我火速辦了婚禮闹获,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘河哑。我一直安慰自己避诽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布灾馒。 她就那樣靜靜地躺著茎用,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睬罗。 梳的紋絲不亂的頭發(fā)上轨功,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音容达,去河邊找鬼古涧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛花盐,可吹牛的內(nèi)容都是我干的羡滑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼算芯,長吁一口氣:“原來是場噩夢啊……” “哼柒昏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熙揍,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤职祷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后届囚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體有梆,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年意系,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泥耀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛔添,死狀恐怖痰催,靈堂內(nèi)的尸體忽然破棺而出兜辞,到底是詐尸還是另有隱情,我是刑警寧澤陨囊,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布弦疮,位于F島的核電站,受9級特大地震影響蜘醋,放射性物質(zhì)發(fā)生泄漏胁塞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一压语、第九天 我趴在偏房一處隱蔽的房頂上張望啸罢。 院中可真熱鬧,春花似錦胎食、人聲如沸扰才。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衩匣。三九已至,卻和暖如春粥航,著一層夾襖步出監(jiān)牢的瞬間琅捏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工递雀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柄延,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓缀程,卻偏偏與公主長得像搜吧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子杨凑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

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

  • 架空小言滤奈,純屬虛構(gòu) chapter31 留家(2) 紹珩在酒店外廊的臺階上等了不到十分鐘,匡家的車子便到了撩满。他見匡...
    春衫冷閱讀 5,787評論 3 3
  • 2017年3月勇吊,教育部公布最新的中國公民出國留學(xué)和外國公民留學(xué)中國情況曼追。單從總量來看,和2016年相比汉规,兩項(xiàng)數(shù)據(jù)都...
    劈柴捌哥閱讀 483評論 0 1
  • 《旋風(fēng)女隊(duì)》這部電影主要講述了從小就擁有足球夢的女主人公吳小麗礼殊,因?yàn)樯砀卟粔虻木壒示运保瑹o法做職業(yè)球員后,一心想把足...
    左佳璐閱讀 1,086評論 0 1