AsyncTask解析篇

流程解讀
  • 自定義的繼承類:因為AsyncTask為抽象類形纺,無法直接創(chuàng)建肃廓,必須繼承它實現(xiàn)一個子類般妙。
a. 常見的方法
onPreExecute():在執(zhí)行任務(wù)前調(diào)用阻逮,位于UI線程內(nèi)
doInBackground():執(zhí)行任務(wù)過程,位于非UI線程內(nèi)
publishProgress():發(fā)布任務(wù)進度娩梨,一般在doInBackground()中調(diào)用沿腰,將數(shù)據(jù)傳遞給調(diào)onPregressUpdate()方法
onPregressUpdate():更新進度條,位于UI線程
onPostExecute():doInBackgound()方法完成后調(diào)用狈定,位于UI線程
onCancelled():取消任務(wù)颂龙,位于UI線程
b. 定義類型
class MyTask extends AsyncTask<Params, Progress, Result>
Params為傳入的數(shù)據(jù),比如url纽什;因此措嵌,通常設(shè)置為String類型;
Progress為更新的進度芦缰,通常為Integer類型;
Result為返回的結(jié)果企巢,如果我們希望返回一個文件,那么其對應(yīng)類型為File让蕾;
  • 創(chuàng)建自定義的MyTask類
MyTask task = new MyTask(...);

創(chuàng)建該類浪规,實際上就會調(diào)用AsyncTask的構(gòu)造函數(shù),如下:

//創(chuàng)建一個異步任務(wù)探孝,注意這里必須在UI線程中創(chuàng)建
public AsyncTask(){
    //定義任務(wù)執(zhí)行過程笋婿,注意這里還沒開始執(zhí)行任務(wù),只是定義任務(wù)的執(zhí)行過程
    mWorker = new WorkerRunnable<Params, Result>() {
        @Override
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //交給子類去實現(xiàn)再姑,獲取到結(jié)果
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr){
                mCancelled.set(true);
                throw tr;
            } finally {
                //最后提交結(jié)果
                postResult(result);
            }
            return result;
        }
    };

    //實際上萌抵,這里用FutureTask包裝Callable的目的是為之后提交給線程池執(zhí)行,因為線程池并不接收Callable類型
    mFuture = new FutureTask<Result>(mWorker){
        @Override
        protected void done() {
            try {
                //若任務(wù)并沒有執(zhí)行過元镀,則需要調(diào)用get()方法獲取到結(jié)果绍填,然后再提交結(jié)果
                postResultIfNotInvoked(get());
            } catch (InterruptedException e){
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e){
                throw new RuntimeException("An error occurred while executing doInBackgound", e.getCause());
            } catch (CancellationException e){
                postResultIfNotInvoked(null);
            }
        }
    };
}

在AsyncTask構(gòu)造函數(shù)內(nèi),初始化了兩個mWorkermFuture兩個變量栖疑。我們?nèi)菀卓吹狡渲衜Futrue實際上就是一個包裝mWorker的FutrueTask實例讨永。

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

如上,實際上WorkerRunnable類型即為Callable類型遇革。

  • 執(zhí)行MyTask實例
mTask.execute(url);

其對應(yīng)調(diào)用AsyncTask中的execute方法卿闹,如下:

//外部調(diào)用方法,執(zhí)行任務(wù)
public final AsyncTask<Params, Progress, Result> execute(Params... params){
    return executeOnExecutor(sDefaultExecutor, params);
}

//真正執(zhí)行任務(wù)的地方
private 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 benn executed.");
        }
    }
    //標(biāo)識為正在執(zhí)行
    mStatus = Status.RUNNING;
    //執(zhí)行前調(diào)用
    onPreExecute();
    mWorker.mParams = params;
    //執(zhí)行任務(wù)B芸臁6亡!揪漩!
    exec.execute(mFuture);
    return this;
}

首先我們看到executeOnExecutor中的onPreExecute()方法旋恼,這是任務(wù)前執(zhí)行的方法,然后執(zhí)行exec.execute(mFuture)奄容,我們回到mFuture的定義冰更,我們知道m(xù)Future只是mWorker的一層包裝产徊,目的是為了交給線程池執(zhí)行。而mWorkder中會執(zhí)行result = doInBackground(mParams);蜀细,這個是交給子類去實現(xiàn)舟铜,獲取到結(jié)果。最后再分發(fā)結(jié)果postResult(result);奠衔。

//獲取到Handler對象
private static Handler getHandler(){
    synchronized (AsyncTask.class){
        if (sHandler == null){
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

//提交結(jié)果
private Result postResult(Result result){
    //創(chuàng)建消息
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    //發(fā)送消息
    message.sendToTarget();
    return result;
}

我們可以看到消息將對應(yīng)的結(jié)果通過一個InternalHandler類型來發(fā)送

//定義一個UI線程上的Handler對象
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:
                result.mTask.finish(result.mData[0]);
            break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

如上谆刨,該類型為UI線程上的一個Handler對象,保證發(fā)送的消息可以在UI線程中接收涣觉,我們先看到其MESSAGE_POST_RESULT類型痴荐,調(diào)用result.mTask.finish(result.mData[0]);方法

//任務(wù)執(zhí)行完畢,提交結(jié)果官册,并標(biāo)識
private void finish(Result result){
    if (isCancelled()){
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

我們可以看到在finish方法中生兆,調(diào)用了onPostExecute(result)方法將結(jié)果發(fā)送出去。這樣一個完整的流程就結(jié)束了膝宁。

當(dāng)然鸦难,還有其更新進度方面,我們回到publishProgress()方法

//更新進度员淫,并發(fā)送進度消息
protected final void publishProgress(Progress... values){
    if (!isCancelled()){
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

回到InternalHandler中合蔽,我們可以看到其對應(yīng)的消息類型,會調(diào)用onProgressUpdate()方法

細節(jié)解析
  • AsyncTask默認是使用串行執(zhí)行的方式
//線程執(zhí)行器介返,默認為串行執(zhí)行
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

如何實現(xiàn)串行執(zhí)行拴事?
添加任務(wù)時,將下一個任務(wù)添加到上一個任務(wù)完成后執(zhí)行

//實現(xiàn)串行執(zhí)行
private static class SerialExecutor implements Executor{
    //雙向隊列圣蝎,存儲任務(wù)
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<>();
    //當(dāng)前正在執(zhí)行的任務(wù)
    Runnable mActive;

    @Override
    public void execute(final Runnable r) {
        //添加到隊尾
        //注意:a. 在這里不是直接傳入r刃宵,而是重新定義一個runnable,目的在于當(dāng)前任務(wù)執(zhí)行完后徘公,再從任務(wù)隊列中獲取到下一個牲证,以保證串行
        //     b. 在這里只是重新定義runnable,并沒有開始真正執(zhí)行!9孛妗坦袍!
        mTasks.offer(new Runnable() {
            @Override
            public void run() {
                try {
                    r.run();
                } finally {
                    //獲取到下一個任務(wù)
                    scheduleNext();
                }
            }
        });

        if (mActive == null){
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null){
            //真正開始執(zhí)行任務(wù)是在這里
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}
  • 定義線程池的方法

亮點在于:靜態(tài)化構(gòu)造線程池,這樣當(dāng)構(gòu)建對象時等太,會初始化線程池

//獲取當(dāng)前CPU核心數(shù)
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//定義線程池核心容量
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT-1, 4));
//定義線程池最大容量
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//過剩的空閑線程的存活時間
private static final int KEEP_ALIVE_SECONDS = 30;

//自定義工廠方法捂齐,標(biāo)記線程,以便管理
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//任務(wù)隊列
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(128);
//線程池
public static final Executor THREAD_POOL_EXECUTOR;

//構(gòu)造一個線程池
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
  • 關(guān)于不定參數(shù)的使用
private static class AsyncTaskResult<Data>{
    final AsyncTask mTask;
    final Data[] mData;

    public AsyncTaskResult(AsyncTask mTask, Data... mData) {
        this.mTask = mTask;
        this.mData = mData;
    }
}
使用不定參數(shù)的目的在于在構(gòu)造這樣的類時缩抡,傳入的Data數(shù)據(jù)類型可變:
private Result postResult(Result result){
    //創(chuàng)建消息
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
}

protected final void publishProgress(Progress... values){
    if (!isCancelled()){
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
如上辛燥,我們可以看到構(gòu)造AsyncTaskResult對象時,既可以傳入單個對象,也可以傳入數(shù)組對象挎塌。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市内边,隨后出現(xiàn)的幾起案子榴都,更是在濱河造成了極大的恐慌,老刑警劉巖漠其,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘴高,死亡現(xiàn)場離奇詭異,居然都是意外死亡和屎,警方通過查閱死者的電腦和手機拴驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柴信,“玉大人套啤,你說我怎么就攤上這事∷娉#” “怎么了潜沦?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绪氛。 經(jīng)常有香客問我唆鸡,道長,這世上最難降的妖魔是什么枣察? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任争占,我火速辦了婚禮,結(jié)果婚禮上序目,老公的妹妹穿的比我還像新娘臂痕。我一直安慰自己,他們只是感情好宛琅,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布刻蟹。 她就那樣靜靜地躺著,像睡著了一般嘿辟。 火紅的嫁衣襯著肌膚如雪舆瘪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天红伦,我揣著相機與錄音英古,去河邊找鬼。 笑死昙读,一個胖子當(dāng)著我的面吹牛召调,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼唠叛,長吁一口氣:“原來是場噩夢啊……” “哼只嚣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起艺沼,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤册舞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后障般,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體调鲸,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年挽荡,在試婚紗的時候發(fā)現(xiàn)自己被綠了藐石。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡定拟,死狀恐怖于微,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情办素,我是刑警寧澤角雷,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站性穿,受9級特大地震影響勺三,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜需曾,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一吗坚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呆万,春花似錦商源、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至出爹,卻和暖如春庄吼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背严就。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工总寻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梢为。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓渐行,卻偏偏與公主長得像轰坊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子祟印,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

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