AsyncTask源碼解析

優(yōu)缺點:

優(yōu)點
它封裝了Handler牧愁,使創(chuàng)建異步任務變得更加簡單逞姿,相較于Handler+Thread的模式妻顶,不再需要編寫任務線程和Handler實例即可完成相同的任務。
缺點

  • 不適合長時間的后臺操作(適合幾秒鐘的操作)
  • AsyncTask的生命周期沒有跟Activity的生命周期同步
    AsyncTask不會隨著activity的銷毀而銷毀匈子,它會一直執(zhí)行, 直到doInBackground()方法執(zhí)行完畢河胎。如果在Activity銷毀之前,沒有取消 AsyncTask虎敦,有可能讓我們的AsyncTask崩潰(crash)游岳。因為它在onPostExecute()中想要處理的view已經不存在了。所以原茅,我們總是必須確保在銷毀活動之前取消任務吭历。
    另外,即使我們正確地調用了cancle() 也未必能真正地取消任務擂橘。因為如果在doInBackgroud里有一個不可中斷的操作晌区,比如BitmapFactory.decodeStream(),那么這個操作會繼續(xù)下去通贞。
  • 容易內存泄漏
    如果AsyncTask被聲明為Activity的非靜態(tài)的內部類朗若,那么AsyncTask會保留一個對創(chuàng)建了AsyncTask的Activity的引用。如果Activity已經被銷毀昌罩,AsyncTask的后臺線程還在執(zhí)行哭懈,它將繼續(xù)在內存里保留這個引用,導致Activity無法被回收茎用,引起內存泄露遣总。
  • 結果丟失
    屏幕旋轉或Activity在后臺被系統殺掉等情況會導致Activity的重新創(chuàng)建,之前運行的AsyncTask會持有一個之前Activity的引用轨功,這個引用已經無效旭斥,這時調用onPostExecute()再去更新界面將不再生效。

要注意的地方:

  • 并行還是串行
    在Android 1.6之前的版本古涧,AsyncTask是串行的垂券,在1.6至2.3的版本,改成了并行的羡滑。在2.3之后的版本又做了修改菇爪,可以支持并行和串行算芯,當想要串行執(zhí)行時,直接執(zhí)行execute()方法凳宙,如果需要并行執(zhí)行熙揍,則要執(zhí)行executeOnExecutor(Executor)。
  • 在activity的destroy()中執(zhí)行cancel()后近速,AsyncTask將執(zhí)行完doInBackground()诈嘿,但不再執(zhí)行onPostExecute()和onProgressUpdate(),而是執(zhí)行onCancelled().

使用:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private ProgressDialog mDialog;
    private TextView mTextView;
    MyAsyncTask myAsyncTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.id_tv);
        mDialog = new ProgressDialog(this);
        mDialog.setMax(100);
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mDialog.setCancelable(true);
        myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (myAsyncTask != null)
            myAsyncTask.cancel(true);
    }

    private class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected void onPreExecute() {
            mDialog.show();
            Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
        }

        @Override
        protected Void doInBackground(Void... params) {

            // 模擬數據的加載,耗時的任務
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(80);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i);
            }

            Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mDialog.setProgress(values[0]);
            Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
        }

        @Override
        protected void onPostExecute(Void result) {
            // 進行數據加載完成后的UI操作
            mDialog.dismiss();
            mTextView.setText("LOAD DATA SUCCESS ");
            Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
        }
    }
}

效果:


源碼分析:

好了削葱,上述我們基本得到了AsyncTask的執(zhí)行流程奖亚,現在我們通過源碼并且結合上述案例,對AsyncTask做更為細致的分析:

一析砸、 首先我們是在主線程上創(chuàng)建了一個繼承AsyncTask的MyAsynTask類: MyAsynTask myAsynTask = new MyAsynTask();由于是繼承關系昔字,上述方法執(zhí)行了MyAsynTask的父類AsyncTask的構造方法:

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);
            }
        }
    };
}
  • AsyncTask類初始化了兩個類:mWorker 與mFuture:
  • WorkerRunnable是實現了Callable接口的抽象方法;FutureTask實現了RunnableFuture首繁,而RunnableFuture接口繼承了Runnable和Future
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> 

public interface RunnableFuture<V> extends Runnable, Future<V>
public class FutureTask<V> implements RunnableFuture<V>

二作郭、 調用myAsynTask對象的execute()方法,并且傳遞參數huhx弦疮;我們跟進去夹攒,發(fā)現實際的執(zhí)行是:executeOnExecutor(sDefaultExecutor, params);

public final 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 been executed "
                        + "(a task can be executed only once)");
        }
    }
    mStatus = Status.RUNNING;
    onPreExecute();
    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}
  • 該方法首先是判斷mStatus狀態(tài),如果是正在運行(RUNNING)或者已經結束運行(FINISHED)胁塞,就會拋出異常咏尝。
  • 接著設置狀態(tài)為運行,執(zhí)行onPreExecute()方法啸罢,并把參數的值賦給mWorker.mParams;
  • 于是Executor去執(zhí)行execute的方法编检,學過java多線程的都知道。這個方法是開啟一個線程去執(zhí)行mFuture中的run()方法
  • 注意mFuture和mWorker都是在AsyncTask的構造方法中初始化過的

三扰才、根據上述講解允懂,將執(zhí)行mFuture的run,也就是將執(zhí)行mWorker的call方法衩匣,如下:

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);
}
  • 設置最高優(yōu)先級蕾总,接著執(zhí)行了doInBackground()方法,注意它的參數mParams正是mWorker的屬性mParams琅捏,而我們在之前有過這樣的代碼:mWorker.mParams = params;因此doInBackground方法的參數就是execute方法傳遞的參數
  • 好了生百,執(zhí)行到了我們重寫的doInBackground()方法了,現在要回到MyAsynTask類中的來了午绳,在doInBackground中執(zhí)行了publishProgress方法。跟進去映之,我們看一下代碼
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

四拦焚、 上述我們講到了Handler的部分了蜡坊,很自然的我們是不是要看一下Handler的handleMessage方法呢?跟進去赎败,我們看一下

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秕衙,所以result.mTask.onProgressUpdate(result.mData);得到執(zhí)行,那么result.mTas是什么了僵刮,對了据忘,就是我們的AsyncTask。由于AsyncTaskResult的第二個參數是values是publishProgress的參數搞糕,那么onProgressUpdate中的參數就是publishProgress方法的參數勇吊,如下:

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

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

五、 好了窍仰,我們要回到第三步的汉规,最后一個postResult(result)方法了,在這個方法當中驹吮,如下

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

看到沒有针史,發(fā)送了MESSAGE_POST_RESULT信息,于是在第六步中的handleMessage方法的代碼中result.mTask.finish(result.mData[0])得到執(zhí)行碟狞,在這個方法中啄枕,執(zhí)行了AsyncTask的finish方法

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

在這代碼中,如果沒有取消族沃,那么就執(zhí)行onPostExecute方法频祝,記得result是什么嗎?Result result = doInBackground(mParams);正是doInBackground方法返回的結果
最后將狀態(tài)設置為Status.FINISHED竭业,還記得我們在AsyncTask的簡要說明的第四點說過嗎智润?一個任務實例只能執(zhí)行一次,如果執(zhí)行第二次將會拋出異常未辆,因為執(zhí)行完一次之后窟绷,狀態(tài)變成FINISHED,在executeOnExecutor方法中會有如下判斷:會報異常的咐柜!

case FINISHED:
        throw new IllegalStateException("Cannot execute task:"
                + " the task has already been executed "
                + "(a task can be executed only once)");
}

六兼蜈、對于在任務的取消中那些說明,我們額外去對它的源碼做一些簡單的分析:
調用onCancelled(true)時拙友,系統會發(fā)送MESSAGE_POST_RESULT信息为狸,也就是提前進入了上述第五步:執(zhí)行如下代碼

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

由于設置了onCancelled(true),所以onCancelled(result)方法得到執(zhí)行遗契。之后再設置狀態(tài)為Status.FINISHED;

建議閱讀

Java多線程:Executor辐棒,Executors,Future,Callable,Runnable等

參考

為什么Android AsyncTask的使用要遵循五大原則

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市漾根,隨后出現的幾起案子泰涂,更是在濱河造成了極大的恐慌,老刑警劉巖辐怕,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逼蒙,死亡現場離奇詭異,居然都是意外死亡寄疏,警方通過查閱死者的電腦和手機是牢,發(fā)現死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陕截,“玉大人驳棱,你說我怎么就攤上這事∷也撸” “怎么了蹈胡?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長朋蔫。 經常有香客問我罚渐,道長,這世上最難降的妖魔是什么驯妄? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任荷并,我火速辦了婚禮,結果婚禮上青扔,老公的妹妹穿的比我還像新娘源织。我一直安慰自己,他們只是感情好微猖,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布谈息。 她就那樣靜靜地躺著,像睡著了一般凛剥。 火紅的嫁衣襯著肌膚如雪侠仇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天犁珠,我揣著相機與錄音逻炊,去河邊找鬼。 笑死犁享,一個胖子當著我的面吹牛余素,可吹牛的內容都是我干的。 我是一名探鬼主播炊昆,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼桨吊,長吁一口氣:“原來是場噩夢啊……” “哼威根!你這毒婦竟也來了?” 一聲冷哼從身側響起视乐,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤医窿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后炊林,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡卷要,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年渣聚,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片僧叉。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡奕枝,死狀恐怖,靈堂內的尸體忽然破棺而出瓶堕,到底是詐尸還是另有隱情隘道,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布郎笆,位于F島的核電站谭梗,受9級特大地震影響,放射性物質發(fā)生泄漏宛蚓。R本人自食惡果不足惜激捏,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凄吏。 院中可真熱鬧远舅,春花似錦、人聲如沸痕钢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽任连。三九已至蚤吹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間课梳,已是汗流浹背距辆。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留暮刃,地道東北人跨算。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像椭懊,于是被迫代替她去往敵國和親诸蚕。 傳聞我的和親對象是個殘疾皇子步势,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內容