4.AsyncTask使用,原理

資料

概述:

Android中试疙,實現(xiàn)異步有兩種方式柳弄,Handler和AsyncTask。
使用Handler+Thread時两蟀,每次執(zhí)行耗時操作都需要創(chuàng)建新的線程進(jìn)行處理网梢,代碼比較臃腫,性能開銷也會較大赂毯。
使用AsyncTask战虏,內(nèi)部是封裝好的Handler+Thread,并且使用了線程池党涕,有效的優(yōu)化了性能烦感。

"AsyncTask并不適合進(jìn)行特別耗時的后臺任務(wù),對特別耗時的任務(wù)膛堤,建議使用線程池手趣。--開發(fā)藝術(shù)探索上所寫,但是還并未理解"
可能是因為:多個任務(wù)時特別耗時的任務(wù)會影響后面的任務(wù)遲遲不能執(zhí)行肥荔。用線程池并發(fā)的話绿渣,就沒這樣的問題了朝群。

原理:

輕量級的異步任務(wù)類,在線程池中串行執(zhí)行任務(wù)中符,通過Handler把進(jìn)度和執(zhí)行結(jié)果發(fā)送到主線程姜胖,更新UI。

簡單使用:

 MyAsyncTask mAsyncTask = new MyAsyncTask();
mAsyncTask.execute();//下面后臺任務(wù)參數(shù)為Void類型淀散,所以不填參數(shù)右莱。
 
/**
*AsyncTask<Void, Integer, Boolean>:泛型,可以為任意類型吧凉。
*分別為:后臺任務(wù)執(zhí)行的參數(shù)隧出,更新進(jìn)度的參數(shù),任務(wù)執(zhí)行完成的結(jié)果
*/
public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{
        private ProgressDialog mProgress;
        private int currentProgress;
        //任務(wù)開始前的準(zhǔn)備操作阀捅,可以做些UI的準(zhǔn)備或初始化胀瞪。
        //多個任務(wù)時,無論是否等待饲鄙,execute()的時候就在主線程調(diào)用
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mProgress.setMax(100);
            mProgress.setMessage("下載進(jìn)度");
            mProgress.show();
        }

        //線程池中執(zhí)行任務(wù)
        @Override
        protected Boolean doInBackground(Void... params) {
            while (currentProgress <= 100) {
                //不能跟新UI
                //mProgress.setProgress(currentProgress);
                //想更新進(jìn)度的話凄诞,主動調(diào)用該方法,會通過handler給onProgressUpdate方法忍级。
                publishProgress(currentProgress);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                   return false;
                }
                currentProgress++;
            }
            return true;
        }

        //publishProgress通過handler調(diào)用帆谍,主線程
        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgress.setMessage("下載進(jìn)度:"+values[0]);
        }

        //執(zhí)行完成后的結(jié)果。
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            if (aBoolean) {
                mProgress.setMessage("下載完成");
            }else {
                mProgress.setMessage("下載出錯");
            }

                mProgress.cancel();
        }
        
        //中斷任務(wù)時轴咱,調(diào)用該方法汛蝙。但并不一定會馬上中斷。
        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }

原理朴肺,源碼分析:

按照調(diào)用方法窖剑,一步一步的分析。

  • 構(gòu)造函數(shù)入手:
public AsyncTask() {
        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);
                }
            }
        };
    }

好像并不能看出來什么戈稿,就兩個實例對象初始化西土。這里記得就行,從內(nèi)部邏輯上調(diào)用的方法名來看,一個mWorker跟后臺任務(wù)有關(guān)鞍盗;一個mFuture跟結(jié)果通知有關(guān)需了。這里看不懂正常,也不要每步都要求詳細(xì)清楚般甲,往下看肋乍。

  • 調(diào)用mAsyncTask.execute()方法:
方法1:    
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

方法2:    
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //如果狀態(tài)為正在運行或者已經(jīng)結(jié)束,報錯敷存。說明住拭,只能調(diào)用一次mAsyncTask.execute()方法。
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:  the task is already running.");
                case FINISHED://明確說明只能執(zhí)行一次。
                    throw new IllegalStateException("Cannot execute task: the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }
        
        mStatus = Status.RUNNING;//改變狀態(tài)

        //看好了哈滔岳,在此處調(diào)用杠娱。此時還是在主線程中哦,所以可以操作UI谱煤。
        onPreExecute();

        //構(gòu)造方法中的一個實例摊求,把執(zhí)行后臺任務(wù)的參數(shù)給了它。
        mWorker.mParams = params;
        //該exec是方法1中的sDefaultExecutor刘离,看下面代碼.
        exec.execute(mFuture);

        return this;
    }
    

1.只能執(zhí)行一次mAsyncTask.execute()方法室叉。
2.主線程調(diào)用onPreExecute()
3.更改狀態(tài),賦值硫惕。
4.調(diào)用線程池有關(guān)方法執(zhí)行茧痕。sDefaultExecutor.execute(mFuture)
繼續(xù)往下看,調(diào)用的方法:

    private static volatile Executor sDefaultExecutor = new SerialExecutor();
    //Executor恼除?線程池類踪旷?
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        //mFuture參數(shù),也即是構(gòu)造方法中的實例
        public synchronized void execute(final Runnable r) {
            //封裝成一個Runnable,放到隊列的最后豁辉。
語句1:        mTasks.offer(new Runnable() {
                public void run() {
                    try {
                    //mFuture的run方法令野,只是調(diào)用方法,run并沒有開新線程
語句5:              r.run();
                    } finally {
語句6:              scheduleNext();
                    }
                }
            });
            //第一次執(zhí)行時
            if (mActive == null) {
語句2:             scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //取出隊列中第一個封裝好的Runnable
語句3:     if ((mActive = mTasks.poll()) != null) {
                //THREAD_POOL_EXECUTOR是線程池
語句4:        THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

執(zhí)行步驟徽级,按語句順序:將mFuture的run方法封裝成Runnable對象放入隊列最后--->第一次執(zhí)行時气破,取出Runnable并到線程池中執(zhí)行--->執(zhí)行完以后,再取餐抢,再去執(zhí)行现使。以此類推...
需要注意:封裝好的Runnable在線程池中會開新的線程。mFuture的run只是一個方法旷痕,也就是說此時run中的代碼已經(jīng)在子線程了碳锈。
上面整個過程都是在放入/取出Runnable,最主要的是執(zhí)行的mFuture的run方法苦蒿。

//FutureTask類殴胧,mFuture的run方法
public void run() {
        if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
語句1:    Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
語句2:             result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
語句3:             set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

語句1中的callable是構(gòu)造函數(shù)中初始化mFuture的用到的第一個實例mWorker啊渗稍。
語句2執(zhí)行了mWorker的call方法佩迟。
語句3最后執(zhí)行了mFuture的done方法。
語句2,3執(zhí)行的正好是構(gòu)造時兩個實例復(fù)寫的方法:先執(zhí)行call竿屹,再執(zhí)行done报强。
再回過頭來看AsyncTask的構(gòu)造方法:

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
語句0           mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
語句1:             result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
語句2:            postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
語句3:           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就是生成AsyncTask實例時復(fù)寫的后臺執(zhí)行任務(wù)的方法:doInBackground() 在線程池中執(zhí)行的。

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

語句2拿到后臺任務(wù)執(zhí)行后的result拱燃,通過mainHandler調(diào)用onPostExecute(result);

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


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;
        }
    }
 }
 
 
private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
    
    protected void onPostExecute(Result result) {
    }

另外秉溉,語句3的 postResultIfNotInvoked(get()),如果滿足條件,也會調(diào)用postResult通過handler....
但是召嘶,正是因為語句0的設(shè)置父晶,使其并不滿足條件,所以不執(zhí)行弄跌。(不太清楚wasTaskInvoked的作用)

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

至此甲喝,關(guān)于調(diào)用mAsyncTask.execute()方法,上面的源碼分析完了铛只,先把任務(wù)放入隊列最后埠胖,然后取出隊列第一個,在線程池中調(diào)用了doInBackground淳玩,后通過主線程的handler調(diào)用onPostExecute(Result result);

  • publishProgress:該方法可以再doInbackGroud中給onProgressUpdate更新進(jìn)度直撤。上面分析的過程中,程序并不會自動調(diào)用該方法蜕着,需要用到的時候谋竖,我們自己調(diào)用就行了。
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
    
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;
            }
        }
        
protected void onProgressUpdate(Progress... values) {
    }

至此侮东,整個AsyncTask執(zhí)行過程就分析完了圈盔。跨度太大悄雅,總結(jié)一下:
1.onPreExecute()在mAsyncTask.execute(...params)執(zhí)行的時候驱敲,馬上執(zhí)行,主線程宽闲。
2.doInBackgroun在線程池中調(diào)用众眨。子線程。
3.得到結(jié)果后容诬,通過主線程的handler發(fā)送消息娩梨,調(diào)用onPostExecute。主線程览徒。
4.手動調(diào)用publishProgress會通過主線程的handler調(diào)用onProgressUpdate狈定。 主線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末习蓬,一起剝皮案震驚了整個濱河市纽什,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躲叼,老刑警劉巖芦缰,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異枫慷,居然都是意外死亡让蕾,警方通過查閱死者的電腦和手機浪规,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來探孝,“玉大人笋婿,你說我怎么就攤上這事《俾” “怎么了萌抵?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長元镀。 經(jīng)常有香客問我绍填,道長,這世上最難降的妖魔是什么栖疑? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任讨永,我火速辦了婚禮,結(jié)果婚禮上遇革,老公的妹妹穿的比我還像新娘卿闹。我一直安慰自己,他們只是感情好萝快,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布锻霎。 她就那樣靜靜地躺著,像睡著了一般揪漩。 火紅的嫁衣襯著肌膚如雪旋恼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天奄容,我揣著相機與錄音冰更,去河邊找鬼。 笑死昂勒,一個胖子當(dāng)著我的面吹牛蜀细,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播戈盈,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼奠衔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了塘娶?” 一聲冷哼從身側(cè)響起归斤,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎血柳,沒想到半個月后官册,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體生兆,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡难捌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年膝宁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片根吁。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡员淫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出击敌,到底是詐尸還是另有隱情介返,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布沃斤,位于F島的核電站圣蝎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衡瓶。R本人自食惡果不足惜徘公,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哮针。 院中可真熱鬧关面,春花似錦、人聲如沸十厢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛮放。三九已至缩抡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間包颁,已是汗流浹背缝其。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徘六,地道東北人内边。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像待锈,于是被迫代替她去往敵國和親漠其。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

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