安卓源碼分析——AsyncTask

1 AsyncTask基礎(chǔ)

1.1 AsyncTask作用

AsyncTask是安卓開發(fā)中使用的一種輕量級異步任務(wù)類朱浴。其作用是在線程池中執(zhí)行后臺任務(wù)罐韩,并在執(zhí)行過程中將執(zhí)行進度傳遞給主線程徊哑,當任務(wù)執(zhí)行完畢后,將最終結(jié)果傳遞給主線程壶熏。


1.2 AsyncTask產(chǎn)生背景

安卓系統(tǒng)線程分為主線程和子線程陶夜,主線程也叫UI線程。主線程主要負責與用戶交互常侣。為了更好的用戶體驗茵肃,保證系統(tǒng)不因主線程的阻塞而產(chǎn)生卡頓,安卓系統(tǒng)要求主線程中不能執(zhí)行耗時任務(wù)袭祟。例如:IO操作验残、網(wǎng)絡(luò)請求等必須在子線程中完成。AsyncTask就是為了適應(yīng)這種需要而產(chǎn)生巾乳。


1.3 AsyncTask使用場景

AsyncTask是一種輕量級的異步線程您没,雖然使用AsyncTask可以更加方便的執(zhí)行后臺任務(wù)與在主線程中訪問UI,但是AsyncTask不適合執(zhí)行特別耗時的后臺任務(wù)胆绊。具體原因會在后續(xù)分析中闡明氨鹏。


1.4 AsyncTask使用方式

AsyncTask原型:

public abstract class AsyncTask<Params, Progress, Result>

由原型可見AsyncTask是一個泛型抽象類。
參數(shù)說明:

Params 執(zhí)行后臺任務(wù)所需參數(shù)類型
Progress 后臺任務(wù)執(zhí)行進度的類型
Result 后臺任務(wù)執(zhí)行完畢后返回結(jié)果類型
AsyncTask 核心方法:

AsyncTask提供4個核心方法:
1) protected void onPreExecute()

執(zhí)行線程 主線程
調(diào)用時間 異步任務(wù)執(zhí)行之前
方法作用 異步任務(wù)執(zhí)行前的初始化工作

2)protected Result doInBackground(Params...params)

執(zhí)行線程 線程池中執(zhí)行
調(diào)用時間 任務(wù)開始后到任務(wù)結(jié)束之前
方法作用 用于執(zhí)行異步任務(wù)

3)protected void onProgressUpdate(Prgress...values)

執(zhí)行線程 主線程
調(diào)用時間 任務(wù)開始后到任務(wù)結(jié)束之前
方法作用 用于更新任務(wù)進度

4)protected void onPostExecute(Result result)

執(zhí)行線程 主線程
調(diào)用時間 異步任務(wù)執(zhí)行之后
方法作用 將異步任務(wù)的執(zhí)行結(jié)果傳遞給主線程

了解各個參數(shù)以及方法含義后我們來看一段官方給出的AsyncTask示例程序:

    private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
        //任務(wù)執(zhí)行前的準備工作
        protected void onPreExecute(){
        }
        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會調(diào)用onProgressUpdate
                 publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
              return totalSize;
          }
     
         //主線程中調(diào)用仆抵,主要用于更新后臺任務(wù)進度
         protected void onProgressUpdate(Integer... progress) {
              setProgressPercent(progress[0]);
          }
     
          //任務(wù)執(zhí)行完畢
          protected void onPostExecute(Long result) {
              showDialog("Downloaded " + result + " bytes");
          }
      }

DownloadFilesTask類模擬文件下載過程。傳入的參數(shù)Params類型為URL(文件地址)种冬,后臺任務(wù)進程參數(shù)Progress類型為Integer(下載進度)镣丑,后臺任務(wù)返回結(jié)果參數(shù)Result類型為Long(總文件大小)。


1.5 AsyncTask使用注意

通過簡單的官方示例程序?qū)W習了AsyncTask的使用方法娱两,那么使用AsyncTask需要注意以下幾個條件:
1)AsyncTask類必須在主線程加載
2)AsyncTask對象必須在主線程創(chuàng)建
3)execute方法必須在主線程調(diào)用
4)不要在程序中直接調(diào)用AsyncTask提供的4個核心方法
5)一個AsyncTask對象只能執(zhí)行一次莺匠,即只能調(diào)用一次execute


1.6 小結(jié)

通過本節(jié)的介紹,對AsyncTask有了初步的認識十兢,第二部分將從AsyncTask源碼出發(fā)趣竣,分析AsyncTask的工作原理以及回答本節(jié)中留下的問題摇庙。


2 源碼分析

在第1小節(jié)中主要介紹了AsyncTask的基本知識,本節(jié)將從源碼出發(fā)遥缕,深層次分析AsyncTask的工作原理卫袒。


2.1 AsyncTask對象創(chuàng)建

使用AsyncTask時,一般根據(jù)具體的任務(wù)繼承AsyncTask類单匣,例如:

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

        protected String doInBackground(String... args1) {
        
            Log.i(TAG, "doInBackground in:" + args1[0]);
            int times = 10;
            for (int i = 0; i < times; i++) {
                publishProgress(i);//提交之后玛臂,會執(zhí)行onProcessUpdate方法
            }
            Log.i(TAG, "doInBackground out");
            return "over";
        }

        /**
         * 在調(diào)用cancel方法后會執(zhí)行到這里
         */
        protected void onCancelled() {
            Log.i(TAG, "onCancelled");
        }

        /**
         * 在doInbackground之后執(zhí)行
         */
        protected void onPostExecute(String args3) {
            Log.i(TAG, "onPostExecute:" + args3);
        }

        /**
         * 在doInBackground之前執(zhí)行
         */
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute");
        }

        /**       
         * @param args2
         */
        @Override
        protected void onProgressUpdate(Integer... args2) {
            Log.i(TAG, "onProgressUpdate:" + args2[0]);
        }
    }

MyAsyncTask對象的創(chuàng)建必須在主線程中,使用方式為:

new MyAsyncTask().execute("AsyncTask Test");

2.2 execute方法

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
     return executeOnExecutor(sDefaultExecutor, params);
}

execute方法調(diào)用了executeOnExecutor方法并傳遞參數(shù)sDefaultExecutor和params封孙。
再看executeOnExecutor方法:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
            
        //判斷當前狀態(tài)
        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)");
            }
        }

        //將狀態(tài)置為運行態(tài)
        mStatus = Status.RUNNING;

        //主線程中最先調(diào)用onPreExecute方法,進行準備工作
        onPreExecute();

        //將參數(shù)傳給mWorker
        mWorker.mParams = params;
        
        //調(diào)用線程池讽营,執(zhí)行任務(wù)
        exec.execute(mFuture);

        return this;
}

executeOnExecutor方法首先判斷狀態(tài)虎忌,若處于可執(zhí)行態(tài),則將狀態(tài)置為RUNNING橱鹏。然后調(diào)用了onPreExecute方法膜蠢,交給用戶進行執(zhí)行任務(wù)前的準備工作。核心部分在于 exec.execute(mFuture)莉兰。exec即sDefaultExecutor挑围。


2.3 sDefaultExecutor串行線程池

查看sDefaultExecutor定義:

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

sDefaultExecutor是一個串行線程池,作用在于任務(wù)的排隊執(zhí)行糖荒。其源碼如下:

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

從SerialExecutor的源碼可以看出杉辙,mFuture是插入到mTasks任務(wù)隊列的對象。當mTasks中沒有正在活動的AsyncTask任務(wù)捶朵,則調(diào)用scheduleNext方法執(zhí)行下一個任務(wù)蜘矢。若一個AsyncTask任務(wù)執(zhí)行完畢,則繼續(xù)執(zhí)行下一個AsyncTask任務(wù)综看,直至所有任務(wù)執(zhí)行完畢品腹。通過分析可以發(fā)現(xiàn)真正去執(zhí)行后臺任務(wù)的是線程池THREAD_POOL_EXECUTOR。


2.4 線程池THREAD_POOL_EXECUTOR

THREAD_POOL_EXECUTOR定義如下:

    public static final Executor THREAD_POOL_EXECUTOR;

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

2.5 mFuture并發(fā)執(zhí)行對象

線程池THREAD_POOL_EXECUTOR中執(zhí)行的Runnable對象為mFuture红碑。

mFuture的定義:

private final FutureTask<Result> mFuture;

mFuture為線程池執(zhí)行的真正任務(wù)舞吭,mFuture的執(zhí)行過程過程是怎樣的呢?

再看AsyncTask的構(gòu)造:

    public AsyncTask(@Nullable Looper callbackLooper) {
    
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        //創(chuàng)建mworker對象
        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
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };
        //創(chuàng)建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);
                }
            }
        };
    }

通過AsyncTask的構(gòu)造中可以看到mWorker為FutureTask的構(gòu)造函數(shù)參數(shù)析珊,則FutureTask對象中持有mWorker的引用羡鸥。

FutureTask的構(gòu)造函數(shù):

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;     
    }

FutureTask的run方法:

 public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask的run方法中調(diào)用了 result = c.call(); 即調(diào)用了mWorker的call方法。


2.6 mWorker對象

mWorker的call方法:

        mWorker = new WorkerRunnable<Params, Result>() {
        
            //call方法
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //執(zhí)行任務(wù)
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

通過代碼可以發(fā)現(xiàn)忠寻,最終在線程池中執(zhí)行的是mWorker的call方法兄春,call方法中調(diào)用了doInBackground方法,因此可以看出doInBackground方法是在線程池中調(diào)用的锡溯。
當任務(wù)執(zhí)行完畢后則調(diào)用postResult方法:

    //任務(wù)執(zhí)行完后調(diào)用方法
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
    

postResult方法發(fā)送MESSAGE_POST_RESULT消息和result赶舆。


2.7 InternalHandler接收處理消息

InternalHandler代碼:

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

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

InternalHandler接收到MESSAGE_POST_RESULT時調(diào)用result.mTask.finish(result.mData[0]);


2.8 finish方法:

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

finish方法中若任務(wù)沒有取消則調(diào)用onPostExecute方法發(fā)送結(jié)果哑姚,若任務(wù)取消則調(diào)用onCancelled方法。finish方法是在主線程中執(zhí)行的芜茵。


2.9 onProgressUpdate

通過上述流程已經(jīng)順序找到了onPreExecute叙量、doInBackgroundonPostExecute方法九串,那么onProgressUpdate是如何執(zhí)行的呢绞佩?
首先查看 publishProgress方法:

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

MyAsyncTask示例中在doInBackground中調(diào)用publishProgress方法,publishProgress方法發(fā)送MESSAGE_POST_PROGRESS消息和進度values猪钮,InternalHandler在接收到MESSAGE_POST_PROGRESS消息中調(diào)用onProgressUpdate方法品山。因此onProgressUpdate也是在主線程中調(diào)用。


2.10 小結(jié)

通過上述一步步的源碼分析過程烤低,已經(jīng)掌握了AsyncTask任務(wù)的執(zhí)行過程肘交。AsyncTask中有兩個線程池串行線程池sDefaultExecutor和線程池THREAD_POOL_EXECUTOR。sDefaultExecutor用于任務(wù)的排隊扑馁,THREAD_POOL_EXECUTOR真正的執(zhí)行任務(wù)涯呻。線程的切換使用Handler(InternalHandler)實現(xiàn)。


3 問題解答

1)為什么AsyncTask在主線程創(chuàng)建執(zhí)行腻要?
因為AsyncTask需要在主線程創(chuàng)建InternalHandler复罐,以便onProgressUpdateonPostExecute 雄家, onCancelled 可以正常更新UI效诅。
2)為什么AsyncTask不適合特別耗時任務(wù)?
AsyncTask實際上是一個線程池趟济。如果有線程長時間占用填帽,且沒有空閑,則其他線程只能處于等待狀態(tài)咙好,會造成阻塞篡腌。
3)AsyncTask內(nèi)存泄漏問題
如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會保留一個對創(chuàng)建了AsyncTask的Activity的引用勾效。如果Activity已經(jīng)被銷毀嘹悼,AsyncTask的后臺線程還在執(zhí)行,它將繼續(xù)在內(nèi)存里保留這個引用层宫,導致Activity無法被回收杨伙,引起內(nèi)存泄露。


總結(jié)

通過對AsyncTask源碼分析萌腿,學習了AsyncTask的工作原理限匣,以及在使用AsyncTask的注意事項。希望通過本篇文章的學習可以加深對于AsyncTask的理解毁菱,學習其設(shè)計思想米死。如果您覺得有用锌历,請點贊支持一下,您的支持就是我的動力峦筒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末究西,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子物喷,更是在濱河造成了極大的恐慌卤材,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峦失,死亡現(xiàn)場離奇詭異扇丛,居然都是意外死亡,警方通過查閱死者的電腦和手機尉辑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門帆精,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人材蹬,你說我怎么就攤上這事×吡停” “怎么了堤器?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長末贾。 經(jīng)常有香客問我闸溃,道長,這世上最難降的妖魔是什么拱撵? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任辉川,我火速辦了婚禮,結(jié)果婚禮上拴测,老公的妹妹穿的比我還像新娘乓旗。我一直安慰自己,他們只是感情好集索,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布屿愚。 她就那樣靜靜地躺著,像睡著了一般务荆。 火紅的嫁衣襯著肌膚如雪妆距。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天函匕,我揣著相機與錄音娱据,去河邊找鬼。 笑死盅惜,一個胖子當著我的面吹牛中剩,可吹牛的內(nèi)容都是我干的忌穿。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼咽安,長吁一口氣:“原來是場噩夢啊……” “哼伴网!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妆棒,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤澡腾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后糕珊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體动分,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年红选,在試婚紗的時候發(fā)現(xiàn)自己被綠了澜公。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡喇肋,死狀恐怖坟乾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝶防,我是刑警寧澤甚侣,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站间学,受9級特大地震影響殷费,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜低葫,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一详羡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘿悬,春花似錦实柠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至躯概,卻和暖如春登钥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娶靡。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工牧牢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓塔鳍,卻偏偏與公主長得像伯铣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子轮纫,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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