AsyncTask分析簡略

引用:(來自源碼文件)

 * AsyncTask可以在UI線程中使用。它通過操作thread和handler可以執(zhí)行后臺工作并將結(jié)果發(fā)布到UI線程壳坪。
 * AsyncTask被設(shè)計為圍繞Thread和Handler進行工作而不是單純的線程池框架舶得。
AsyncTask適用于最多耗時幾秒的這種短操作。
如果你想保持線程的長連接爽蝴,建議使用java.util.concurrent包提供的Executor沐批、ThreadPoolExecutor和FutureTask纫骑。
 * 一個異步操作的定義是在工作線程執(zhí)行計算操作并且在主線程返回結(jié)果。
它由三個參數(shù)Params九孩、Progress和Result組成先馆。
由四個步驟onPreExecute、doInBackground躺彬、onProgressUpdate和onPostExecute組成煤墙。
* AsyncTask必須由子類繼承使用。子類至少要實現(xiàn)doInBackground方法宪拥,并且經(jīng)常會用到onPostExecute方法仿野。
下面是一個實現(xiàn)子類:
 * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
 *     protected Long doInBackground(URL... urls) {
 *         int count = urls.length;
 *         long totalSize = 0;
 *         for (int i = 0; i < count; i++) {
 *             totalSize += Downloader.downloadFile(urls[i]);
 *             publishProgress((int) ((i / (float) count) * 100));
 *             // Escape early if cancel() is called
 *             if (isCancelled()) break;
 *         }
 *         return totalSize;
 *     }
 *
 *     protected void onProgressUpdate(Integer... progress) {
 *         setProgressPercent(progress[0]);
 *     }
 *
 *     protected void onPostExecute(Long result) {
 *         showDialog("Downloaded " + result + " bytes");
 *     }
 * }

* 一旦創(chuàng)建了實例,可以通過execute方法啟動她君。
* 一個異步任務(wù)要有這三類數(shù)據(jù):
Params:發(fā)送給異步任務(wù)的參數(shù)设预。
Progress:在后臺任務(wù)計算的時候發(fā)布出來的單位進度。
Result:后臺任務(wù)執(zhí)行完畢后返回的結(jié)果犁河。
并不是每一個都要有鳖枕。可以通過使用Void來略過桨螺。
* 4個過程步驟宾符。當(dāng)一個異步任務(wù)執(zhí)行的時候會經(jīng)過這四個方法。
onPreExecute()
doInBackground
onProgressUpdate
onPostExecute
onPreExecute:任務(wù)執(zhí)行之前在UI線程調(diào)用灭翔。這個回調(diào)通常用于任務(wù)配置魏烫,比如在用戶界面顯示一個進度條。
doInBackground:onPreExecute方法執(zhí)行完畢后在后臺線程立即調(diào)用肝箱。這一步用于后臺費時計算操作哄褒。這一步會傳入之前的參數(shù)。
計算的結(jié)果必須被return返回傳遞給下一步煌张。這里也可以使用publishProgress方法發(fā)布一個或多個進度單位呐赡。這些值會被傳入UI線程的onProgressUpdate方法。
onProgressUpdate:在調(diào)用publishProgress方法后此方法在UI進程調(diào)用骏融。此方法的執(zhí)行時間是不確定的链嘀。用戶可以在后臺任務(wù)執(zhí)行時在
這個方法里隨意更新進度條。例如你能夠顯示進度動畫或者顯示進度log档玻。
onPostExecute:在后臺任務(wù)結(jié)束后怀泊,此方法在UI線程執(zhí)行。計算的結(jié)果會通過參數(shù)傳到這里误趴。
* 取消任務(wù)霹琼。
可以在任何時候調(diào)用cancel方法取消任務(wù)。調(diào)用這個方法之后,onInBackground執(zhí)行后就調(diào)用onCanceled方法而不是onPostExecute枣申。
為了確保任務(wù)能夠即使取消树灶,應(yīng)該在doInBackground中持續(xù)檢測isCancelled方法。
* 線程規(guī)則糯而。為了確保線程能夠正確運行應(yīng)該遵守下面幾個規(guī)則天通。
AsyncTask必須在UI線程中啟動。在版本16之后會自動這樣做熄驼。
任務(wù)實例必須在UI線程中創(chuàng)建像寒。
execute方法必須在UI線程中調(diào)用。
不要主動調(diào)用onPreExecute和onPostExecute方法和doInBackground和onProgressUpdate瓜贾。
一個task任務(wù)只能執(zhí)行一次诺祸,再次啟動會拋出異常。
版本4開始AsynckTasks是串行執(zhí)行的祭芦,版本11之后就改成了線程池并發(fā)執(zhí)行筷笨,
 * 串行執(zhí)行可以避免一些應(yīng)用問題,如果實在想并發(fā)執(zhí)行龟劲,可以使用executeOnExecutor(Executor, Object[])方法

源碼部分:
先看源碼中所有static靜態(tài)對象

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     * 翻譯:并發(fā)執(zhí)行的Executor
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

上面的靜態(tài)數(shù)據(jù)都是為了創(chuàng)建并發(fā)線程池執(zhí)行器THREAD_POOL_EXECUTOR的胃夏。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

聲明并創(chuàng)建串型線程執(zhí)行器SERIAL_EXECUTOR

@MainThread
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

使用默認線程執(zhí)行器執(zhí)行指定的Runnable。默認執(zhí)行器默認是串型的昌跌。該方法只能在UI線程調(diào)用仰禀。

//Handler定義為static靜態(tài)防止持有外部類導(dǎo)致內(nèi)存溢出
private static InternalHandler sHandler;
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;
            }
        }
    }

創(chuàng)建靜態(tài)內(nèi)部Handler對象,構(gòu)造中使用了Looper.getMainLooper()蚕愤,也就是綁定了UI線程的Looper對象答恶,所有Handler是執(zhí)行在UI線程中的,而不是當(dāng)前子線程萍诱。handleMessage方法處理兩個事件:1悬嗓、返回第一個Result結(jié)果數(shù)據(jù)。2裕坊、返回進度Progress數(shù)據(jù)包竹。兩個方法都是運行在UI線程。

     /**
     * @param <Params>
     * @param <Result>
     *
     *     Callable與Runanble沒有什么不同碍庵,只是Runnable沒有返回映企,Callable有返回,而且Callable只能配合ExecuotrService在線程池中使用静浴。
     */
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

聲明WorkerRunnable<Params,Result>工作線程類,繼承自Callable<Result>挤渐,Callable與Runanble沒有什么不同苹享,只是Runnable沒有返回,Callable有返回,而且Callable只能配合ExecuotrService在線程池中使用得问。
聲明AsyncTaskResult結(jié)果封裝類囤攀,封裝了一個AsyncTask對象mTask和它對應(yīng)的Data結(jié)果數(shù)組。

    public AsyncTask() {
        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);
            }
        };

        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);
                }
            }
        };
    }

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

AsyncTask構(gòu)造器宫纬,也是初始化的地方焚挠,會初始化一個WorkerRunnable對象,因為它繼承自Callable漓骚,所以重寫call方法蝌衔,在call方法中會執(zhí)行doInBackground()方法,也就是是線程中執(zhí)行的耗時操作蝌蹂,并調(diào)用postResult返回doInBackground的Result結(jié)果噩斟。
初始化一個FutureTask對象,F(xiàn)utureTask繼承了Runnable和Future孤个。所以可以執(zhí)行run方法也可以通過get取回結(jié)果剃允。重寫計算結(jié)束時調(diào)用的done方法,在里面調(diào)用postResultIfNotInvoked(get())齐鲤,它最后也會執(zhí)行postResult方法返回結(jié)果斥废。
postResult方法就是將當(dāng)前對象和結(jié)果封裝成AsyncTaskResult對象并發(fā)送給內(nèi)部Handler。在Handle中就會調(diào)用onPostExecute(result)方法给郊。

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

在doInBackground()執(zhí)行子線程耗時任務(wù)時营袜,可以直接調(diào)用publishPorgress方法發(fā)送進度消息給內(nèi)部Handler,Handler就是執(zhí)行UI線程的onProgressUpdate方法更新進度了丑罪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荚板,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吩屹,更是在濱河造成了極大的恐慌跪另,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煤搜,死亡現(xiàn)場離奇詭異免绿,居然都是意外死亡,警方通過查閱死者的電腦和手機擦盾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門嘲驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迹卢,你說我怎么就攤上這事辽故。” “怎么了腐碱?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵誊垢,是天一觀的道長。 經(jīng)常有香客問我,道長喂走,這世上最難降的妖魔是什么殃饿? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮芋肠,結(jié)果婚禮上乎芳,老公的妹妹穿的比我還像新娘。我一直安慰自己帖池,他們只是感情好奈惑,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碘裕,像睡著了一般携取。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帮孔,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天雷滋,我揣著相機與錄音,去河邊找鬼文兢。 笑死晤斩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姆坚。 我是一名探鬼主播澳泵,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼兼呵!你這毒婦竟也來了兔辅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤击喂,失蹤者是張志新(化名)和其女友劉穎维苔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懂昂,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡介时,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凌彬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沸柔。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铲敛,靈堂內(nèi)的尸體忽然破棺而出褐澎,到底是詐尸還是另有隱情,我是刑警寧澤原探,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布乱凿,位于F島的核電站顽素,受9級特大地震影響咽弦,放射性物質(zhì)發(fā)生泄漏徒蟆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一型型、第九天 我趴在偏房一處隱蔽的房頂上張望段审。 院中可真熱鬧,春花似錦闹蒜、人聲如沸寺枉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姥闪。三九已至,卻和暖如春砌烁,著一層夾襖步出監(jiān)牢的瞬間筐喳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工函喉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留避归,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓管呵,卻偏偏與公主長得像梳毙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子捐下,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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