手撕AsyncTask

前言

AsyncTask作為日常中一個經(jīng)常被使用到的類,知道他的工作原理也是一件非常重要的事情。

AsyncTask的使用代碼講解

AsyncTask使用方法已經(jīng)寫在下方的代碼中硝全。

/**
 * AsyncTask存在三個泛型類型
 * 1栖雾。Params:輸入數(shù)據(jù)
 * 2。Progress:類似于用于進度更新的返回值
 * 3伟众。Result:輸出的數(shù)據(jù)
 */
public class MyTask extends AsyncTask<String, Integer, String> {

    /**
     * 后臺操作
     * @param strings
     * @return
     */
    @Override
    protected String doInBackground(String... strings) {
        return null;
    }

    /**
     * 執(zhí)行前的預處理
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    /**
     * 執(zhí)行完成
     * @param s
     */
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }

    /**
     * 刷新得到的新更新
     * @param values
     */
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    /**
     * 任務取消
     * @param s
     */
    @Override
    protected void onCancelled(String s) {
        super.onCancelled(s);
    }

    /**
     * 任務取消
     */
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }
}

而Async的執(zhí)行流程如下圖所示


一般我們在代碼中只用執(zhí)行excute()的函數(shù)析藕,在各個函數(shù)流程中給出相對應的操作。

對應的項目寫在我的Github倉庫

原理揭秘

MyTask task = new MyTask();
task.execute("赂鲤。噪径。");

步驟一:構造函數(shù)

在使用期間我們是需要實例化這個對象的,那么必經(jīng)的入口就是他的構造函數(shù)了数初。

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                // 找爱。。泡孩。
                // 這里出現(xiàn)了我必須要重寫的函數(shù)3瞪恪!仑鸥!
                result = doInBackground(mParams);
                // 吮播。。眼俊。
                // 把我們得到的數(shù)據(jù)傳輸了出去意狠,發(fā)送到哪里呢?
                postResult(result);
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                // 疮胖。环戈。。
                // 一個一定會執(zhí)行的函數(shù)
                postResultIfNotInvoked(get());
                // 澎灸。院塞。。
            }
        };
    }
    
    private void postResultIfNotInvoked(Result result) {
        // 任務標記
        final boolean wasTaskInvoked = mTaskInvoked.get();
        // 任務為執(zhí)行性昭,則傳遞結果
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

在這里我看到的都是一些數(shù)據(jù)的初始化拦止,其實就是對任務完成的出口發(fā)送消息的定義、數(shù)據(jù)如何進行傳遞的定義等等糜颠。

步驟二:執(zhí)行汹族、execute()

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

進入執(zhí)行的第一句話,我們就能夠看到這樣一個函數(shù)其兴,從字面意思我就能夠知道了鞠抑,是將數(shù)據(jù)放入了一個線程池。
為了驗證我們之前所說的執(zhí)行流程我們先進入executeOnExecutor ()的函數(shù)忌警。

    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        // 。。法绵。箕速。
        // 驗證了最開始的使用流程,excute執(zhí)行后朋譬,調(diào)用的是onPreExecute
        onPreExecute();
        // 開始對任務進行了處理
        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

回到我們的執(zhí)行流程中盐茎,我們還沒有了解我們用的線程池他是怎樣的?
通過不斷的函數(shù)調(diào)用徙赢,我們能夠找到這樣的一個線程池內(nèi)部類字柠。

    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(); // 1
                    }
                }
            });
            if (mActive == null) {
                scheduleNext(); // 1
            }
        }

        protected synchronized void scheduleNext() {
            // 從任務隊列中取出一個任務
            if ((mActive = mTasks.poll()) != null) {
                // 出現(xiàn)了另外一個線程池來進行執(zhí)行?狡赐?窑业??
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

其他我們先并不予以關注枕屉,你是否看到了scheduleNext()這個一定會被調(diào)用的函數(shù)呢常柄?而且里面又出現(xiàn)了一個線程池。原來這個THREAD_POOL_EXECUTOR變量才是真正的執(zhí)行動作的線程池搀擂,那怎么去看待上面的那個線程池呢西潘?其實mTasks這個變量已經(jīng)告訴我們答案了,他是一個進程存儲的進程任務隊列哨颂,并且execute()是一個同步函數(shù)喷市,所以SerialExecutor確切的說是一個進行按序任務調(diào)度的線程池。這樣也就證實了在很多博客中會說AsyncTask是一個由兩個線程池和一個Handler組成的威恼。

調(diào)用順序的驗證

前面講過了execute()后面調(diào)用的函數(shù)是onPreExecute()品姓。而接下來的步驟就是doInBackground(),回到先前的execute()的代碼中沃测,我們知道會出現(xiàn)下面的代碼

exec.execute(mFuture); 

// 上方操作調(diào)用的函數(shù)
public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
}

我們已經(jīng)講過了sDefaultExecutor他是一個任務調(diào)度的線程池缭黔,他會將任務發(fā)送給THREAD_POOL_EXECUTOR進行完成,而他完成的mFuture其實也是一個全局變量蒂破,我在構造函數(shù)中已經(jīng)提到過了馏谨。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    // 由線程池執(zhí)行完成時,用于回調(diào)的借口
                    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);
                }
            }
        };

這里給讀者們看個全貌附迷,作為最后的回調(diào)接口中惧互,我們是否已經(jīng)關注到了一個函數(shù)doInBackground()這個函數(shù)呢?
目前為止我們已經(jīng)完成了前三步喇伯,就差最后的一步onPostExecute()的執(zhí)行出現(xiàn)了喊儡,其實在上文就已經(jīng)提到過了postResult(result)這段代碼,就是進行一個結果的提交也就對應著我們所說的onPostExecute()稻据。
我們通過深入了解可以知道艾猜,就是通過Handler將一個數(shù)據(jù)傳出去,這個時候既然結束了,那就對應的是一個finish()函數(shù)匆赃。

private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // 任務結束淤毛,返回數(shù)據(jù)。
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    // 得到了publish的操作算柳,來到這里操作數(shù)據(jù)低淡。
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
// 被調(diào)用的finish函數(shù)
private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

到這里,我們完完整整的驗證了執(zhí)行的流程瞬项。

總結

以上就是我的學習成果蔗蹋,如果有什么我沒有思考到的地方或是文章內(nèi)存在錯誤,歡迎與我分享囱淋。


相關文章推薦:
手撕OkHttp
手撕ButterKnife
HandlerThread那些事兒
手撕Handler

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猪杭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绎橘,更是在濱河造成了極大的恐慌胁孙,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件称鳞,死亡現(xiàn)場離奇詭異涮较,居然都是意外死亡,警方通過查閱死者的電腦和手機冈止,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門狂票,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熙暴,你說我怎么就攤上這事闺属。” “怎么了周霉?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵掂器,是天一觀的道長。 經(jīng)常有香客問我俱箱,道長国瓮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任狞谱,我火速辦了婚禮乃摹,結果婚禮上,老公的妹妹穿的比我還像新娘跟衅。我一直安慰自己孵睬,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布伶跷。 她就那樣靜靜地躺著掰读,像睡著了一般秘狞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蹈集,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天谒撼,我揣著相機與錄音,去河邊找鬼雾狈。 笑死,一個胖子當著我的面吹牛抵皱,可吹牛的內(nèi)容都是我干的善榛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼呻畸,長吁一口氣:“原來是場噩夢啊……” “哼移盆!你這毒婦竟也來了?” 一聲冷哼從身側響起伤为,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤咒循,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绞愚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叙甸,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年位衩,在試婚紗的時候發(fā)現(xiàn)自己被綠了裆蒸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡糖驴,死狀恐怖僚祷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贮缕,我是刑警寧澤辙谜,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站感昼,受9級特大地震影響装哆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抑诸,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一烂琴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜕乡,春花似錦奸绷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽反症。三九已至,卻和暖如春畔派,著一層夾襖步出監(jiān)牢的瞬間铅碍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工线椰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胞谈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓憨愉,卻偏偏與公主長得像烦绳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子配紫,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359