AsyncTask分析

今天來分析一下AsyncTask羡玛,并開始嘗試閱讀Google源碼工程師寫的文檔.全英文,顯然是一項(xiàng)挑戰(zhàn)一睁,來吧钻弄。因?yàn)橹耙矝]寫過源碼分析的文章,看的也比較少者吁,所以如果結(jié)構(gòu)混亂窘俺,原諒我不是故意的。


AsyncTask源碼在源碼目錄的framework/base/core/java/android/os目錄下复凳,在具體代碼之前瘤泪,有詳細(xì)的英文說明文檔。這篇文章就出自此處染坯。

一均芽、說明

AsyncTask是Anroid系統(tǒng)提供給我們的在UI線程中使用該類,來進(jìn)行執(zhí)行后臺(tái)執(zhí)行長(zhǎng)時(shí)間的操作单鹿,并將執(zhí)行結(jié)果更新到UI線程中;是除了使用Thread和Handler來實(shí)現(xiàn)耗時(shí)操作和結(jié)果更新的另一種實(shí)現(xiàn)深纲,更簡(jiǎn)單仲锄,方便劲妙。

  • 定義說明:定義一個(gè)異步的AsyncTask任務(wù)時(shí),系統(tǒng)允許傳遞3個(gè)屬性類型來進(jìn)行說明儒喊,Params镣奋,Progress,Result;按其執(zhí)行順序依次對(duì)應(yīng)怀愧,執(zhí)行前需傳入的參數(shù)侨颈,執(zhí)行中進(jìn)度的更新單位,執(zhí)行完成之后返回的結(jié)果類型芯义;使用時(shí)可傳入String哈垢,Long,Integer等類型的參數(shù)扛拨,如果不需要參數(shù)耘分,使用時(shí)可傳Void。
  • 執(zhí)行過程說明:AsyncTask執(zhí)行的過程主要設(shè)計(jì)開發(fā)者的就是4個(gè)方法绑警,也可以理解為4步:依次為onPreExecute求泰,doInBackground,onProgressUpdate计盒,onPostExecute渴频。其中只有doInBackground方法是在非UI線程中執(zhí)行的,其他三個(gè)均在UI線程中執(zhí)行北启,極大的方便了開發(fā)者的使用卜朗。
二、用法

AsyncTask類在源碼中被定義成了抽象類暖庄,如果開發(fā)者想要使用AsyncTask,必須要自己實(shí)現(xiàn)一個(gè)自定義任務(wù)類來extends源碼聊替,然后進(jìn)行使用。自定義任務(wù)類應(yīng)至少實(shí)現(xiàn)doInBackground方法培廓,因?yàn)槠涫浅殚e的惹悄。通常的情況下,還會(huì)實(shí)現(xiàn)onPostExecute方法肩钠,onPreExecute方法等泣港。
舉例:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     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((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
      protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
          showDialog("Downloaded " + result + " bytes");
      }
  }

一旦一個(gè)任務(wù)被創(chuàng)建,開始執(zhí)行該任務(wù)价匠,僅需調(diào)用其execute方法即可執(zhí)行当纱。

  • 執(zhí)行任務(wù)實(shí)例,執(zhí)行任務(wù)分為串行和并行兩種形式踩窖,如下:
new DownAsyncTask().extcute();//串行實(shí)例化
new DownAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");//并行實(shí)例化
  • 取消一個(gè)任務(wù):一個(gè)任務(wù)可以在執(zhí)行期間的任何時(shí)刻被取消坡氯,只需要調(diào)用其cancel方法,調(diào)用cancel方法之后,isCancelled方法就會(huì)返回true箫柳,表示該任務(wù)已取消手形;在調(diào)用cancel方法之后,doInBackground方法結(jié)束執(zhí)行之后悯恍,會(huì)調(diào)用onCancelled方法库糠,不在執(zhí)行onPostExecute方法;
三涮毫、使用規(guī)則謹(jǐn)記
  • 目標(biāo)任務(wù)必須在UI線程中進(jìn)行加載瞬欧;
  • 目標(biāo)任務(wù)實(shí)例必須在UI線程中進(jìn)行創(chuàng)建;
  • 開發(fā)者不能手動(dòng)調(diào)用onPreExecute罢防,onPostExecute艘虎,doInBackground,onProgressUpdate方法篙梢;
  • 目標(biāo)任務(wù)僅僅會(huì)執(zhí)行一次顷帖,如果試圖執(zhí)行多次,系統(tǒng)會(huì)拋出異常
四渤滞、執(zhí)行規(guī)則
  • 起初剛開始設(shè)計(jì)時(shí)贬墩,AsyncTask僅會(huì)在后臺(tái)單獨(dú)的線程中進(jìn)行執(zhí)行,也就是說執(zhí)行是串行的;
  • 從SDK1.6開始妄呕,開始轉(zhuǎn)變?yōu)樵谝粋€(gè)線程池中允許多個(gè)任務(wù)進(jìn)行請(qǐng)求執(zhí)行,也就是支持并行執(zhí)行陶舞;
  • 從3.1開始,又修改為目標(biāo)任務(wù)在一個(gè)單線程中執(zhí)行绪励,也就是串行執(zhí)行肿孵,主要是為了避免并發(fā)執(zhí)行可能會(huì)造成的錯(cuò)誤;如果實(shí)在想并發(fā)執(zhí)行疏魏,可以調(diào)用executeOnExecutor方法進(jìn)行停做;

以上的內(nèi)容均來在AsyncTask源碼聲明文件前的注釋,從中也可以看到大莫,人家Google確實(shí)是偉大的公司蛉腌,標(biāo)準(zhǔn)規(guī)范特別詳細(xì),特別貼心只厘。

五烙丛、代碼分析
  • 關(guān)于線程池的配置
//獲取到當(dāng)前設(shè)備的cpu配置
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//核心線程數(shù)量 動(dòng)態(tài)的計(jì)算 
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
//線程池中允許的最大線程數(shù)目
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
  • 關(guān)于任務(wù)狀態(tài)
    一個(gè)目標(biāo)任務(wù)在其完整的生命周期內(nèi),其任意時(shí)刻的狀態(tài)僅會(huì)是一下三種中的一種:
public enum Status {
        //任務(wù)未執(zhí)行
        PENDING,
        //任務(wù)正在執(zhí)行中
        RUNNING,
        //任務(wù)已結(jié)束
        FINISHED,
    }
  • 構(gòu)造函數(shù)中的初始化工作
public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                //設(shè)置線程的優(yōu)先級(jí)
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                //調(diào)用doInBackground方法,并將postResult進(jìn)行回傳分發(fā)
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            //任務(wù)執(zhí)行完畢后會(huì)調(diào)用done方法
            @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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
      };
}
//初始化操作中涉及到的mWorker變量的類聲明,該WorkerRunnable實(shí)現(xiàn)了Callable接口羔味,使用時(shí)實(shí)現(xiàn)call方法
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

//初始化操作中涉及到的get()方法河咽,該方法返回的就是執(zhí)行后的結(jié)果,也就是mWorker的call方法的返回值
public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

  • 執(zhí)行過程分析
    1在主線程中實(shí)例化任務(wù)對(duì)象并顯示調(diào)用execute方法。
    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     * 全局的線程池用來執(zhí)行隊(duì)列任務(wù)
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    //This method must be invoked on the UI thread.方法注釋說明該方法必須在UI線程調(diào)用
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //調(diào)用executeOnExecutor方法執(zhí)行后臺(tái)任務(wù)
        return executeOnExecutor(sDefaultExecutor, params);
    }

2.執(zhí)行線程池對(duì)象的execute方法赋元,具體執(zhí)行某一個(gè)隊(duì)列任務(wù).

    //包含兩個(gè)參數(shù)忘蟹,第一個(gè)參數(shù)為默認(rèn)的線程池對(duì)象飒房,默認(rèn)已經(jīng)初始化;第二個(gè)參數(shù)為具體任務(wù)的傳入?yún)?shù)
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        //執(zhí)行之前首先判斷任務(wù)的狀態(tài)寒瓦,只有是PENDING狀態(tài)才能執(zhí)行情屹,這里也照應(yīng)了前文說的坪仇,一個(gè)目標(biāo)任務(wù)只能被執(zhí)行一次杂腰,否則會(huì)拋出異常;
        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)");
            }
        }
        //將目標(biāo)任務(wù)的狀態(tài)設(shè)置為正在執(zhí)行
        mStatus = Status.RUNNING;
        //調(diào)用onPreExecute方法椅文,注意此時(shí)還在UI線程中
        onPreExecute();
        //將任務(wù)傳入?yún)?shù)進(jìn)行賦值喂很,調(diào)用execute方法執(zhí)行耗時(shí)操作;同時(shí)返回為AsyncTask對(duì)象任務(wù)自身皆刺;
        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

3.具體的execute方法

    //變量定義
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //Executor 聲明及實(shí)現(xiàn)
    private static class SerialExecutor implements Executor {
        //任務(wù)線程隊(duì)列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        
        //上一步驟提到的execute方法少辣,參數(shù)說明傳入一個(gè)Runnable對(duì)象
        public synchronized void execute(final Runnable r) {
            //將此任務(wù)添加到任務(wù)隊(duì)列中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        //此處調(diào)用的run方法就是調(diào)用的傳入的mFuture對(duì)象的run方法
                        r.run();
                    } finally {
                        //try...finally代碼塊,finally塊一定會(huì)執(zhí)行,處理隊(duì)列中的下一個(gè)任務(wù)
                        scheduleNext();
                    }
                }
            });
            //如果沒有活動(dòng)的runnable羡蛾,從雙端隊(duì)列里面取出一個(gè)runnable放到線程池中運(yùn)行
            //第一個(gè)請(qǐng)求任務(wù)過來的時(shí)候mActive是空的
            if (mActive == null) {
                scheduleNext();
            }
        }
        
        //具體處理下一個(gè)任務(wù)的方法
        protected synchronized void scheduleNext() {
            //從隊(duì)列中取一個(gè)任務(wù) 然后執(zhí)行線程池的execute方法
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    
    //線程池對(duì)象
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

4.關(guān)于mFuture以及具體的線程執(zhí)行和方法調(diào)用

    //上文已經(jīng)提到過的繼承自Callable接口的自定義類漓帅,同時(shí)添加了Params數(shù)組參數(shù);
    private final WorkerRunnable<Params, Result> mWorker;
    //具體的任務(wù)線程對(duì)象
    private final FutureTask<Result> mFuture;
    
    //Java庫中的FutureTask聲明及定義,實(shí)現(xiàn)了RunnableFuture接口痴怨,RunnableFuture接口繼承自Runnable,這也是上一步驟的execute要傳入的對(duì)象mFeature的原因忙干,我們主要看run方法。
    public class FutureTask<V> implements RunnableFuture<V> {
        public void run() {    
          if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
            return;    
          try {   
             //此處涉及到了一個(gè)Callable對(duì)象     
             Callable<V> c = callable;        
             if (c != null && state == NEW) { 
               V result;
               boolean ran;
               try { 
                 //調(diào)用Callable的call方法浪藻。這里就明白為什么我們要看這段代碼了捐迫,就是因?yàn)槲覀円矍宕a執(zhí)行的順序及調(diào)用關(guān)系。
                 //在執(zhí)行run方法的時(shí)候爱葵,會(huì)回調(diào)call方法施戴,其實(shí)就是我們AsyncTask類中以及上文分析的在構(gòu)造方法中初始化操作的mWorkRunnable匿名內(nèi)部類的call方法,然后執(zhí)行其耗時(shí)操作萌丈。
                 //當(dāng)執(zhí)行完成時(shí)再調(diào)用done方法赞哗,進(jìn)行結(jié)果分發(fā)。
                 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);
          }
      }
   }

上面的兩個(gè)步驟比較關(guān)鍵辆雾,解析完代碼肪笋,重新用文字進(jìn)行梳理一下,具體來說就是:exec.execute(mFuture)執(zhí)行時(shí)乾颁,SerialExecutor將實(shí)現(xiàn)了Runnable以及RunnableFeture接口的mFuture傳遞給實(shí)例對(duì)象執(zhí)行execute方法涂乌。在execute方法中,首先將任務(wù)添加到任務(wù)隊(duì)列中英岭,添加方法為mTasks.offer(Runnable r);然后開始執(zhí)行任務(wù)湾盒,第一次執(zhí)行時(shí)mActive==null,所以直接從任務(wù)隊(duì)列中取一個(gè)任務(wù)并調(diào)用線程池的execute方法诅妹,其實(shí)就是mFuture的run方法罚勾,從任務(wù)隊(duì)列取任務(wù)的方法為mTasks.poll()方法毅人;上一個(gè)任務(wù)執(zhí)行完run方法之后,接著處理下一個(gè)任務(wù)尖殃,依次循環(huán)丈莺;在執(zhí)行mFeture的run方法時(shí),也就是子線程開始執(zhí)行時(shí)送丰,會(huì)回調(diào)call方法缔俄,call方法就是上文的mWorkRunnable中的call,從而call方法中又調(diào)用了doInBackground方法執(zhí)行耗時(shí)操作; doInBackground方法執(zhí)行完畢后器躏,會(huì)調(diào)用postResult方法將結(jié)果分發(fā)回傳俐载。

  1. postResult方法以及子線程如何通知主線程的分析
private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
private static InternalHandler sHandler;
//使用了單例模式來保證Handler唯一
private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
private static class InternalHandler extends Handler {
        public InternalHandler() {
            //獲取到UI線程的Looper()
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                //結(jié)果消息分發(fā)
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                //進(jìn)度消息分發(fā)
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

至此,AsyncTask源碼大概的內(nèi)容就是這些登失。歡迎評(píng)論交流遏佣。

掃描下方的二維碼,加入關(guān)注揽浙,所發(fā)布的博客文章會(huì)及時(shí)發(fā)布到公眾號(hào)状婶,方便及時(shí)查看,加入我吧馅巷,一起進(jìn)步膛虫。

喜歡而非堅(jiān)持
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市令杈,隨后出現(xiàn)的幾起案子走敌,更是在濱河造成了極大的恐慌,老刑警劉巖逗噩,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掉丽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡异雁,警方通過查閱死者的電腦和手機(jī)捶障,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纲刀,“玉大人项炼,你說我怎么就攤上這事∈景恚” “怎么了锭部?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)面褐。 經(jīng)常有香客問我拌禾,道長(zhǎng),這世上最難降的妖魔是什么展哭? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任湃窍,我火速辦了婚禮闻蛀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘您市。我一直安慰自己觉痛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布茵休。 她就那樣靜靜地躺著薪棒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪泽篮。 梳的紋絲不亂的頭發(fā)上盗尸,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音帽撑,去河邊找鬼。 笑死鞍时,一個(gè)胖子當(dāng)著我的面吹牛亏拉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逆巍,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼及塘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了锐极?” 一聲冷哼從身側(cè)響起笙僚,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灵再,沒想到半個(gè)月后肋层,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翎迁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年栋猖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汪榔。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒲拉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出痴腌,到底是詐尸還是另有隱情雌团,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布士聪,位于F島的核電站锦援,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏戚嗅。R本人自食惡果不足惜雨涛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一枢舶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧替久,春花似錦凉泄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颅拦,卻和暖如春蒂誉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背距帅。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工右锨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碌秸。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓绍移,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親讥电。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蹂窖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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