AsyncTask原理

一:簡(jiǎn)單說下使用
AsyncTask是Google提供的輕量級(jí)的異步任務(wù)類,該類中實(shí)現(xiàn)了異步操作,并提供接口返回結(jié)果以及進(jìn)度

//這三個(gè)參數(shù)分別指,請(qǐng)求參數(shù)的類型,更新進(jìn)度的類型,返回結(jié)果的類型,沒有就寫Void
    class MyAsyncTask extends AsyncTask<Void,Integer,String>{
        //在子線程中執(zhí)行
        @Override
        protected String doInBackground(Void... params) {
            return null;
        }
        //任務(wù)執(zhí)行前的操作,在main線程執(zhí)行
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
        //返回結(jié)果,在main線程執(zhí)行
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }
        //更新進(jìn)度,在main線程執(zhí)行
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }
    }

使用:

MyAsyncTask task = new MyAsyncTask();
        task.execute();

二:下面主要看下源碼
(1)首先看構(gòu)造方法

 public AsyncTask() {
        //asynctask的初始化就是初始化mWorker和Mfuture
        //這一步可以簡(jiǎn)單的理解為根據(jù)傳入的參數(shù)準(zhǔn)備"Runnable"對(duì)象,然后扔到線程里面去執(zhí)行

        //注意這里只是對(duì)mWorker進(jìn)行初始化,還沒有執(zhí)行,指的是還沒有放入到線程池里面執(zhí)行
        //mWorker封裝的call()方法就是我們要在后臺(tái)執(zhí)行的方法
        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
                    //doInBackground()方法需要我們自己實(shí)現(xiàn)
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //獲得結(jié)果之后通過sHandler發(fā)出去
                    postResult(result);
                }
                return result;
            }
        };

        //FutureTask可用于異步獲取執(zhí)行結(jié)果或取消執(zhí)行任務(wù)的場(chǎng)景趾徽。
        // 通過傳入Runnable或者Callable的任務(wù)給FutureTask,
        // 直接調(diào)用其run方法或者放入線程池執(zhí)行,之后可以在外部通過FutureTask的get方法異步獲取執(zhí)行結(jié)果仑撞,
        // 因此私爷,F(xiàn)utureTask非常適合用于耗時(shí)的計(jì)算扫尖,主線程可以在完成自己的任務(wù)后伐坏,再去獲取結(jié)果。
        // 另外击胜,F(xiàn)utureTask還可以確保即使調(diào)用了多次run方法亏狰,它都只會(huì)執(zhí)行一次Runnable或者Callable任務(wù),
        // 或者通過cancel取消FutureTask的執(zhí)行等偶摔。
        //這里mFuture對(duì)mWorker進(jìn)行封裝,也就是對(duì)mWorker的call()方法封裝,也就是說最終被放入到線程的是mFuture
        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);
                }
            }
        };
    }

(2)然后再看execute()方法,看看mFuture是什么時(shí)候被提交到線程池執(zhí)行的

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
接著點(diǎn):
  public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //如果不是未執(zhí)行的狀態(tài)
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    //記住這個(gè)異常的名字
                    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();//在主線程執(zhí)行,準(zhǔn)確的說,在AysncTask實(shí)例化的線程執(zhí)行
        mWorker.mParams = params;
        //從這里可以看出執(zhí)行mFuture的是exec,那么exec是什么呢?
        exec.execute(mFuture);

        return this;
    }

(3)exec就是sDefaultExecutor
接著看

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

我們先來(lái)看看SerialExecutor的官方解釋:

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */

簡(jiǎn)答說:這是一個(gè)通過串行方式每次執(zhí)行一個(gè)任務(wù)的Executor,而且在某個(gè)進(jìn)程中是全局的.
看看它的實(shí)現(xiàn):

 private static class SerialExecutor implements Executor {
        //這是一個(gè)任務(wù)隊(duì)列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //這個(gè)方法將r對(duì)象添加到隊(duì)列的尾部
            mTasks.offer(new Runnable() {
                //注意,這個(gè)run()方法不執(zhí)行r.run()就不會(huì)執(zhí)行
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //取出隊(duì)列中的第一個(gè)Runnable對(duì)象,這個(gè)mActive就是mTasks.offer(new Runnable() 中new出來(lái)的Runnables
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

也就是說,SerialExecutor內(nèi)部維持一個(gè)任務(wù)隊(duì)列,有任務(wù)來(lái)的時(shí)候就把任務(wù)加入到隊(duì)列里面,然后從任務(wù)隊(duì)列里面取出一個(gè)任務(wù)放入到THREAD_POOL_EXECUTOR執(zhí)行.
THREAD_POOL_EXECUTOR就是一個(gè)線程池,不用多說了.
也就是說SerialExecutor最大的作用就是保證任務(wù)一個(gè)一個(gè)的串行處理,真正處理的是THREAD_POOL_EXECUTOR.
(4)任務(wù)已經(jīng)執(zhí)行了,我們?cè)趺传@取結(jié)果呢?下面再回過頭來(lái)看看最開始的構(gòu)造方法里面的postResult(result)方法

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
//getHandler()這個(gè)方法返回一個(gè)handler,通過handler. obtainMessage()方法獲取一個(gè)target對(duì)象是該handler的Message對(duì)象
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
我們看看getHandler()方法
private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
}
再看看InternalHandler
 private static class InternalHandler extends Handler {
        public InternalHandler() {
            //看過handler源碼的對(duì)這里應(yīng)該比較熟悉,沒看過的建議去看看
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            //根據(jù)msg的不同類型執(zhí)行不同的操作,這里跟我們平常寫handler是一樣的
            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;
            }
        }
    }

三:幾個(gè)問題
(1)AsyncTask的串行并行執(zhí)行問題(源碼中已經(jīng)說明是串行執(zhí)行了)
做個(gè)試驗(yàn):

        MyAsyncTask task1 = new MyAsyncTask();
        task1.execute("1");
        MyAsyncTask task2 = new MyAsyncTask();
        task2.execute("2");
        MyAsyncTask task3 = new MyAsyncTask();
        task3.execute("3");

在doInBackground()方法里做簡(jiǎn)單處理

      protected String doInBackground(String... params) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(params[0]);
            return null;
        }

結(jié)果:

 17:21:50.557 8849-8880/com.dgtech.sss.asynctaskdemo I/System.out: 1
 17:21:51.566 8849-8950/com.dgtech.sss.asynctaskdemo I/System.out: 2
 17:21:52.568 8849-9010/com.dgtech.sss.asynctaskdemo I/System.out: 3

從執(zhí)行結(jié)果(時(shí)間)來(lái)看,是串行的
那么有沒有辦法可以并行執(zhí)行呢?
答案是肯定的,用executeOnExecutor()
這個(gè)方法傳入的第一個(gè)參數(shù)是一個(gè)線程池,第二個(gè)參數(shù)是請(qǐng)求的參數(shù)
使用這個(gè)方法也就是繞過了SerialExecutor這個(gè)類,不再由這個(gè)類進(jìn)行隊(duì)列處理,當(dāng)然就并發(fā)執(zhí)行了
試驗(yàn):

       MyAsyncTask task1 = new MyAsyncTask();
        task1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"1");
        MyAsyncTask task2 = new MyAsyncTask();
        task2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"2");
        MyAsyncTask task3 = new MyAsyncTask();
        task3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"3");

結(jié)果:

17:27:57.265 19387-19408/com.dgtech.sss.asynctaskdemo I/System.out: 1
17:27:57.267 19387-19410/com.dgtech.sss.asynctaskdemo I/System.out: 3
17:27:57.269 19387-19409/com.dgtech.sss.asynctaskdemo I/System.out: 2

(2)一個(gè)AsyncTask對(duì)象只能執(zhí)行一次execute()方法,否則會(huì)報(bào)異常:

"Cannot execute task the task is already running."

(3)防止內(nèi)存泄漏,應(yīng)該在Activity銷毀前執(zhí)行task.cancel(true)取消task的執(zhí)行
(4)當(dāng)內(nèi)存不足或者屏幕旋轉(zhuǎn)導(dǎo)致Activity重建,此時(shí)舊的AsyncTask持有舊的Activity引用,因此不能更新返回結(jié)果更新UI

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暇唾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辰斋,更是在濱河造成了極大的恐慌策州,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宫仗,死亡現(xiàn)場(chǎng)離奇詭異够挂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)藕夫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門孽糖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人毅贮,你說我怎么就攤上這事办悟。” “怎么了嫩码?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵誉尖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我铸题,道長(zhǎng),這世上最難降的妖魔是什么琢感? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任丢间,我火速辦了婚禮,結(jié)果婚禮上驹针,老公的妹妹穿的比我還像新娘烘挫。我一直安慰自己,他們只是感情好柬甥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布饮六。 她就那樣靜靜地躺著,像睡著了一般苛蒲。 火紅的嫁衣襯著肌膚如雪卤橄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天臂外,我揣著相機(jī)與錄音窟扑,去河邊找鬼喇颁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嚎货,可吹牛的內(nèi)容都是我干的橘霎。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼殖属,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姐叁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起洗显,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤七蜘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后墙懂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體橡卤,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年损搬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碧库。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡巧勤,死狀恐怖嵌灰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颅悉,我是刑警寧澤沽瞭,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站剩瓶,受9級(jí)特大地震影響驹溃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜延曙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一豌鹤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧枝缔,春花似錦布疙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至趴荸,卻和暖如春儒溉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赊舶。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工睁搭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赶诊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓园骆,卻偏偏與公主長(zhǎng)得像舔痪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锌唾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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