從Handler探究AsyncTask原理

前言

在之前的文章深入探究了Handler器仗,《從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)》我們知道了Android的消息機(jī)制主要靠Handler來實現(xiàn),但是在Handler的使用中橄妆,忽略內(nèi)存泄露的問題爱榔,不管是代碼量還是理解程度上都顯得有點不盡人意,所以Google官方幫我們在Handler的基礎(chǔ)上封裝出了AsyncTask。但是在使用AsyncTask的時候有很多細(xì)節(jié)需要注意,它的優(yōu)點到底體現(xiàn)在哪里允坚?還是來看看源碼一探究竟。

怎么使用

來一段平常簡單使用AsyncTask來異步操作UI線程的情況蛾号,首先新建一個類繼承AsyncTask,構(gòu)造函數(shù)傳入我們要操作的組件(ProgressBarTextView

class MAsyncTask extends AsyncTask<Void, Integer, String>{
        private ProgressBar mProgressBar;
        private TextView mTextView;

        public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
            this.mProgressBar = mProgressBar;
            this.mTextView = mTextView;
        }

        @Override
        protected void onPreExecute() {
            mTextView.setText("開始執(zhí)行");
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(Void... params) {
            for(int i = 0; i <= 100; i++){
                publishProgress(i);//此行代碼對應(yīng)下面onProgressUpdate方法
                try {
                    Thread.sleep(100);//耗時操作涯雅,如網(wǎng)絡(luò)請求
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "執(zhí)行完畢";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgressBar.setProgress(values[0]);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(String s) {
            mTextView.setText(s);
            super.onPostExecute(s);
        }
    }

在Activity中創(chuàng)建我們新建的MAsyncTask實例并且執(zhí)行(無關(guān)代碼省略):

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
        asyncTask.execute();//開始執(zhí)行
        ...
    }
}

看看原理

在上面的代碼鲜结,我們開了個單一的線程來執(zhí)行了一個簡單的異步更新UI的操作(哈哈,可能會覺得AsyncTask有些大材小用了哈)活逆,現(xiàn)在來看看AsyncTask具體是怎么實現(xiàn)的精刷,先從構(gòu)造方法開始:

public abstract class AsyncTask<Params, Progress, Result> 

AsyncTask為抽象類,并且有三個泛型蔗候,我覺得這三個泛型是很多使用者不懂的根源:

  • params:參數(shù)怒允,在execute() 傳入,可變長參數(shù)锈遥,跟doInBackground(Void... params) 這里的params類型一致纫事,我這里沒有傳參數(shù),所以可以將這個泛型設(shè)置為Void
  • Progress:執(zhí)行的進(jìn)度所灸,跟onProgressUpdate(Integer... values)values的類型一致丽惶,一般情況為Integer
  • Result:返回值,跟String doInBackground 返回的參數(shù)類型一致爬立,且跟onPostExecute(String s)s參數(shù)一致钾唬,在耗時操作執(zhí)行完畢調(diào)用。我這里執(zhí)行完畢返回了個字符串,所以為String

看了這三個泛型抡秆,我們就基本上了解了AsyncTask的執(zhí)行過程奕巍,主要就是上面代碼重寫的那幾個方法,現(xiàn)在來仔細(xì)看儒士,首先在繼承AsyncTask時有個抽象方法必須重寫:

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

顧名思義的止,這個方法是在后臺執(zhí)行,也就是在子線程中執(zhí)行乍桂,需要子類來實現(xiàn)冲杀,在這個方法里面我們可以調(diào)用publishProgress來發(fā)送進(jìn)度給UI線程,并且在onProgressUpdate方法中接收睹酌。

根據(jù)調(diào)用順序权谁,我們一般會重寫這幾個方法:

//在doInBackground之前調(diào)用,在UI線程內(nèi)執(zhí)行
@MainThread
protected void onPreExecute() {
}
//在執(zhí)行中憋沿,且在調(diào)用publishProgress方法時旺芽,在UI線程內(nèi)執(zhí)行,用于更新進(jìn)度
@MainThread
protected void onProgressUpdate(Progress... values) {
}
//在doInBackground之后調(diào)用辐啄,在UI線程內(nèi)執(zhí)行
@MainThread
protected void onPostExecute(Result result) {
}

我們來看看這個publishProgress方法是怎么來調(diào)用onProgressUpdate方法的:

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

使用obtainMessage是避免重復(fù)創(chuàng)建消息采章,調(diào)用了getHandler()然后發(fā)送消息,這里是一個單例

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

返回了一個InternalHandler

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

在判斷消息為MESSAGE_POST_PROGRESS后我們發(fā)現(xiàn)其實內(nèi)部就是調(diào)用了Handler來實現(xiàn)這一切,包括執(zhí)行結(jié)束時調(diào)用finish方法壶辜,這個我們后面再說悯舟。從頭來看一下AsyncTask的執(zhí)行過程,來到execute方法:

/**
This method must be invoked on the UI thread.(這行注釋為Google官方注釋)
*/
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

注意砸民!此方法必須在UI線程調(diào)用抵怎,這里就不做測試了。在這里又調(diào)用executeOnExecutor:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    ......

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

我們發(fā)現(xiàn)在UI線程先調(diào)用了onPreExecute()岭参,將傳入的參數(shù)賦值給mWorker.mParams反惕,然后調(diào)用了參數(shù)exec的execute方法,并且將mFuture作為參數(shù)傳入演侯,這里就設(shè)計到了三個對象:sDefaultExecutor(在executeOnExecutor中傳入)姿染、mWorkermFuture秒际,來看看它們的賦值在哪里:

sDefaultExecutor:

private static volatile Executor sDefaultExecutor = SERIAL_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);
       }
   }
}

我們發(fā)現(xiàn)sDefaultExecutor的賦值默認(rèn)就是SERIAL_EXECUTOR悬赏,也就是一個順序執(zhí)行的線程池,內(nèi)部實現(xiàn)有一個任務(wù)隊列娄徊。

mWorker

private final WorkerRunnable<Params, Result> mWorker;

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

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
}

在AsyncTask的構(gòu)造方法中舷嗡,給mWorker賦值為一個Callable(帶返回參數(shù)的線程,涉及到j(luò)ava并發(fā)的一些基礎(chǔ)知識嵌莉,這里不贅述)进萄,并且在call方法中執(zhí)行了doInBackground方法,最后調(diào)用postResult方法

mFuture

private final FutureTask<Result> mFuture;
public AsyncTask() {
        ......
        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);
                }
            }
        };
}

mFuture為FutureTask類型,這里將mWorker傳入中鼠,在mWorker執(zhí)行完畢后調(diào)用postResultIfNotInvoked方法可婶,我們先看看這個方法:

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

其實這個方法也最后調(diào)用了postResult,在這之前做了個有沒調(diào)用的判斷援雇,確保任務(wù)執(zhí)行完畢后調(diào)用此方法矛渴。來看看postResult方法:

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

又看到了熟悉的obtainMessagesendToTarget發(fā)送消息,這次消息內(nèi)容變?yōu)?strong>MESSAGE_POST_RESULT惫搏,再來看看我們剛才已經(jīng)提到的InternalHandlerhandleMessage方法:

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

最后根據(jù)消息類型具温,這里調(diào)用了result.mTask.finish,result類型為AsyncTaskResult

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

mTask的類型為AsyncTask筐赔,找到AsyncTask的finish方法:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

最后如果沒有取消的話調(diào)用了onPostExecute铣猩,也就是我們之前重寫的那個方法,在執(zhí)行完畢后調(diào)用茴丰,并且此方法也在子線程达皿。

多線程并發(fā)

正如開題所說,AsyncTask本質(zhì)上就是對Handler的封裝贿肩,在執(zhí)行之前峦椰,執(zhí)行中,執(zhí)行完畢都有相應(yīng)的方法汰规,使用起來也一目了然汤功,不過這還并不是AsyncTask的最大的優(yōu)點,AsyncTask最適合使用的場景是多線程溜哮,開始在代碼中已經(jīng)看到了在AsyncTask內(nèi)部有自己維護(hù)的線程池冤竹,默認(rèn)的是SERIAL_EXECUTOR

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

按照順序執(zhí)行,一個任務(wù)執(zhí)行完畢再執(zhí)行下一個茬射,還提供有一個支持并發(fā)的線程池:

//獲取CPU數(shù)目
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心工作線程(同時執(zhí)行的線程數(shù))
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//線程池允許的最大線程數(shù)目
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//空閑線程超時時間(單位為S)
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());
    }
};
//阻塞隊列,用來保存待執(zhí)行的任務(wù)(最高128個)
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

public static final Executor THREAD_POOL_EXECUTOR
 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

聲明為static冒签,多個實例同用一個線程池在抛,這個是Googl官方自帶的一個根據(jù)cpu數(shù)目來優(yōu)化的線程池,使用方法如下:

for(int i = 0; i < 100; i++) {//模擬100個任務(wù)萧恕,不超過128
    MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
    asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}

executeOnExecutor中我們還可以傳入自己自定義的線程池:

//跟默認(rèn)一樣的按順序執(zhí)行
asyncTask.executeOnExecutor(Executors.newSingleThreadExecutor());
//無限制的Executor
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
//同時執(zhí)行數(shù)目為10的Executor
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));

總結(jié)

AsyncTask使用起來的確很簡單方便刚梭,內(nèi)部也是Android的消息機(jī)制,并且很快捷的實現(xiàn)了異步更新UI票唆,特別是多線程時也可以很好的表現(xiàn)朴读,這個是我們單獨使用Handler時不具備的,但是在使用過程中注意內(nèi)部方法的調(diào)用順序以及調(diào)用的時機(jī)走趋,比如asyncTask.execute() 要在UI主線程中調(diào)用衅金,在子線程中調(diào)用是不可以的,還有就是在使用時根據(jù)情況來決定到底應(yīng)該用哪種線程池。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氮唯,一起剝皮案震驚了整個濱河市鉴吹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惩琉,老刑警劉巖豆励,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞒渠,居然都是意外死亡良蒸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門伍玖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫩痰,“玉大人,你說我怎么就攤上這事私沮∈际辏” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵仔燕,是天一觀的道長造垛。 經(jīng)常有香客問我,道長晰搀,這世上最難降的妖魔是什么五辽? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮外恕,結(jié)果婚禮上杆逗,老公的妹妹穿的比我還像新娘。我一直安慰自己鳞疲,他們只是感情好罪郊,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著尚洽,像睡著了一般悔橄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腺毫,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天癣疟,我揣著相機(jī)與錄音,去河邊找鬼潮酒。 笑死睛挚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的急黎。 我是一名探鬼主播扎狱,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼侧到,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了委乌?” 一聲冷哼從身側(cè)響起床牧,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遭贸,沒想到半個月后戈咳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡壕吹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年著蛙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耳贬。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡踏堡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咒劲,到底是詐尸還是另有隱情顷蟆,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布腐魂,位于F島的核電站帐偎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛔屹。R本人自食惡果不足惜削樊,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兔毒。 院中可真熱鬧漫贞,春花似錦、人聲如沸育叁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豪嗽。三九已至谴蔑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昵骤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工肯适, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留变秦,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓框舔,卻偏偏與公主長得像蹦玫,于是被迫代替她去往敵國和親赎婚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

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