AsyncTask為什么可以在回調(diào)中修改UI(源碼分析)

今天偶然想到了使用AsyncTask異步任務(wù)棧,可以在onPostExecute()中修改UI,這不是一個(gè)很奇怪的現(xiàn)象嗎怎披?于是便萌發(fā)想法看看源碼這到底是怎么回事。

一瓶摆、AsyncTask的使用介紹

還記得AsyncTask的使用方法不钳枕?主要是重寫幾個(gè)方法

protected void onPreExecute() //開始前

protected void onProgressUpdate() //進(jìn)行中
            
protected Object doInBackground() //后臺(tái)執(zhí)行的具體邏輯

protected void onPostExecute() //任務(wù)執(zhí)行完畢

protected void onCancelled() //任務(wù)取消
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
    private ViewHolder v;

    @Override
    protected Bitmap doInBackground(ViewHolder... params) {
        v = params[0];
        return mFakeImageLoader.getImage();
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        if (v.position == position) {
            // If this item hasn't been recycled already, hide the
            // progress and set and show the image
            v.progress.setVisibility(View.GONE);
            v.icon.setVisibility(View.VISIBLE);
            v.icon.setImageBitmap(result);
        }
    }
}.execute(holder);

二、源碼中找答案

  1. AsyncTask在其中打開Structure視圖赏壹,馬上就看到了有個(gè)getHandler()方法還有一個(gè)成員變量InternalHandler鱼炒。于是馬上猜測AsyncTask,其實(shí)是使用了Handler機(jī)制實(shí)現(xiàn)了線程切換蝌借。


    image
  2. 繼續(xù)看InternalHandler的源碼昔瞧,已經(jīng)發(fā)現(xiàn)端倪,它已經(jīng)直接調(diào)用了onProgressUpdate()方法菩佑。

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;
            }
        }
    }
  1. finish方法里有什么呢自晰?繼續(xù)看,果然調(diào)用onPostExecute()方法稍坯,至此酬荞,基本已經(jīng)確定AsyncTask能夠在回調(diào)中修改UI就是使用了Handler機(jī)制。
 private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
  1. 什么時(shí)候向Handler發(fā)的消息呢瞧哟?看AsyncTask構(gòu)造方法就可以了混巧。構(gòu)造方法里初始化了工作線程和FutureTask。
 public AsyncTask() {
        //創(chuàng)建一個(gè)工作線程
        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); //執(zhí)行完了調(diào)用postResult
            }
        };
        //放入FutureTask中
        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);
                }
            }
        };
    }
  1. 在executeOnExecutor方法中開始執(zhí)行勤揩。
    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); //exec是傳入的線程池

        return this;
    }
  1. 最后看下第4步中的postResult()方法的源碼咧党,找到怎么給Handler發(fā)消息的。到這里整個(gè)鏈條已經(jīng)串起來陨亡。
  private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget(); //發(fā)送消息
        return result;
    }

三傍衡、總結(jié)

AsyncTask并沒有違反Android定義的非UI線程不能修改UI的規(guī)則,實(shí)際上它是使用了Handler機(jī)制實(shí)現(xiàn)了線程切換负蠕。在onPostExecute() 實(shí)際上已經(jīng)處于UI線程當(dāng)中蛙埂,所以可以對(duì)UI進(jìn)行修改,同理于onProgressUpdate()方法遮糖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绣的,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌被辑,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敬惦,死亡現(xiàn)場離奇詭異盼理,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)俄删,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門宏怔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人畴椰,你說我怎么就攤上這事臊诊。” “怎么了斜脂?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵抓艳,是天一觀的道長。 經(jīng)常有香客問我帚戳,道長玷或,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任片任,我火速辦了婚禮偏友,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘对供。我一直安慰自己位他,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布产场。 她就那樣靜靜地躺著鹅髓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪京景。 梳的紋絲不亂的頭發(fā)上迈勋,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音醋粟,去河邊找鬼靡菇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛米愿,可吹牛的內(nèi)容都是我干的厦凤。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼育苟,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼较鼓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤博烂,失蹤者是張志新(化名)和其女友劉穎香椎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禽篱,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畜伐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躺率。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玛界。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悼吱,靈堂內(nèi)的尸體忽然破棺而出慎框,到底是詐尸還是另有隱情,我是刑警寧澤后添,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布笨枯,位于F島的核電站,受9級(jí)特大地震影響遇西,放射性物質(zhì)發(fā)生泄漏猎醇。R本人自食惡果不足惜努溃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梧税。 院中可真熱鬧沦疾,春花似錦第队、人聲如沸哮塞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尸执。三九已至家凯,卻和暖如春如失,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褪贵。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國打工掂之, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人世舰。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓动雹,卻偏偏與公主長得像跟压,于是被迫代替她去往敵國和親胰蝠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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

  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 3,219評(píng)論 1 15
  • Android開發(fā)者:你真的會(huì)用AsyncTask嗎裆馒? 導(dǎo)讀.1 在Android應(yīng)用開發(fā)中,我們需要時(shí)刻注意保證...
    cxm11閱讀 2,716評(píng)論 0 29
  • 在Android中我們可以通過Thread+Handler實(shí)現(xiàn)多線程通信丐怯,一種經(jīng)典的使用場景是:在新線程中進(jìn)行耗時(shí)...
    呂侯爺閱讀 2,057評(píng)論 2 23
  • 我有個(gè)朋友喷好,她的朋友是一個(gè)矮萌妹子,叫相相读跷。今天我們就來說說這個(gè)相相的故事梗搅。 看人嘛,首先是長相效览,我認(rèn)識(shí)她的時(shí)候无切,...
    北辰北閱讀 420評(píng)論 0 1
  • 感謝裸貸給了我們作人的尊嚴(yán)。 也許許多人動(dòng)不動(dòng)就寫寫抨擊我們的文章丐枉,自己卻偷偷地欣賞著我們那10個(gè)G的資源哆键;也許許...
    學(xué)好奇門遁甲閱讀 254評(píng)論 4 1