AsyncTask源碼分析

突然發(fā)現(xiàn)自己開發(fā)其實(shí)很少用到AsyncTask?(? ???ω??? ?)在這里補(bǔ)上對它的源碼分析

初見

在Android啟蒙導(dǎo)師黑馬程序員的視頻中曾經(jīng)聽到過一句話虫几,AsyncTask:輕量級的線程工具暂论,方便快捷的實(shí)現(xiàn)工作線程到主線程的通信召烂。
在翻看AsyncTask源碼時(shí)一部分注釋如下

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
 * to perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 *
 * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
 * and does not constitute a generic threading framework. AsyncTasks should ideally be
 * used for short operations (a few seconds at the most.) 

大意呢就是AsyncTask可以允許你在工作線程中發(fā)布結(jié)果到UI線程咽弦,但是它適合短時(shí)間操作最多幾秒鐘,嗨喲,這么傲嬌,走了走了绍申,還是滾去用RxJava算了,HandlerThread也行顾彰。
AsyncTask:別看注釋嚇人极阅,我這樣都沒被淘汰,親兒子啊~~
那我們再留下看看他還有什么話要說(;?_?)

使用

可以看到AsyncTask需要傳入3個(gè)泛型參數(shù)
public abstract class AsyncTask<Params, Progress, Result>
依次代表傳入工作線程的參數(shù)涨享,進(jìn)度回調(diào)筋搏,回調(diào)給UI線程的結(jié)果,艾瑪厕隧,都有進(jìn)度回調(diào)奔脐,這么一看好像很NB的樣子。
我們隨便寫個(gè)AsyncTask看看

TestTask task = new TestTask();
task.execute(1, 2, 3, 4, 5, 6);
/**
     * 測試
     */
    class TestTask extends AsyncTask<Integer, Integer, String> {
        private static final String TAG = "TestTask";

        @Override
        protected void onPreExecute() {
            //預(yù)處理(UI線程)
        }

        @Override
        protected String doInBackground(Integer... params) {
            //必須實(shí)現(xiàn)吁讨,異步耗時(shí)任務(wù)(工作線程)
            StringBuilder sb = new StringBuilder();
            for (int i : params) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sb.append(i);
            }
            Log.d(TAG, "doInBackground: " + sb.toString());
            return sb.toString();
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //進(jìn)度回調(diào)(UI線程)
            Log.d(TAG, "onProgressUpdate: " + values[0]);
        }

        @Override
        protected void onPostExecute(String s) {
            //回調(diào)給UI線程(UI線程)
            Log.d(TAG, "onPostExecute: " + s);
        }
    }

過了一會果然輸出了2行日志髓迎,進(jìn)度什么的還是要自己來搞
doInBackground: 123456
onPostExecute: 123456

分析

那么我們來看看execute這個(gè)方法是在怎么啟動的

//UI線程限定
 @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
 @MainThread
    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;
    }

看到這里,我們有了大概的思路建丧,execute方法內(nèi)部調(diào)用了默認(rèn)的執(zhí)行方法來執(zhí)行排龄,相對狀態(tài)進(jìn)行校驗(yàn)過后,將參數(shù)賦值后翎朱,執(zhí)行方法涣雕,我們接下來先看看參數(shù)是在怎么賦值的艰亮,
mWorker.mParams = params;

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

可以發(fā)現(xiàn)mWorker其實(shí)就就是簡簡單單一個(gè)實(shí)現(xiàn)Callback接口的靜態(tài)內(nèi)部類,里面存放參數(shù)挣郭,那么exec.execute(mFuture);呢迄埃,execute傳遞進(jìn)去了一個(gè)默認(rèn)的執(zhí)行規(guī)則

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//execute 默認(rèn)
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

其中Executor是一個(gè)接口

public interface Executor {
    void execute(Runnable command);
}

SerialExecutor,翻譯出來叫做串行執(zhí)行兑障,我們來瞧瞧TA的實(shí)現(xiàn)

private static class SerialExecutor implements Executor {
        //雙端隊(duì)列侄非,可以簡單的理解成一個(gè)隊(duì)列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //正在運(yùn)行中的
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //將一個(gè)runnable放置到隊(duì)列末尾
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //不考慮執(zhí)行結(jié)果如何都會執(zhí)行下一個(gè)任務(wù)
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                //第一次從這里開始執(zhí)行任務(wù)
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //從隊(duì)列取出一個(gè)任務(wù)
            if ((mActive = mTasks.poll()) != null) {
                //真正開始執(zhí)行
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

看到這里,敢情SerialExecutor就是THREAD_POOL_EXECUTOR的封裝版流译,保證里面的runnable都是串行執(zhí)行逞怨,名至實(shí)歸,看到這里福澡,筆者突然發(fā)現(xiàn)小小的AsyncTask中蘊(yùn)含了不少的東西叠赦,那么我們完整的看下AsyncTask的構(gòu)造器和成員變量

深入

//API25
public abstract class AsyncTask<Params, Progress, Result> {
    //手機(jī)CPU數(shù)目
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //根據(jù)CPU來配置核心線程數(shù)目,每個(gè)編譯版本可能不一樣
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //線程池最大的容納數(shù)量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    //空線程30s备镌遥活
    private static final int KEEP_ALIVE_SECONDS = 30;
    //配置線程池時(shí)除秀,給每個(gè)線程名字命名
    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());
        }
    };
    //阻塞隊(duì)列,限長128
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    //線程池的執(zhí)行
    public static final Executor THREAD_POOL_EXECUTOR;
    //靜態(tài)代碼塊算利,全局都是這個(gè)線程池
    static {
        //將上述的參數(shù)作為構(gòu)造參數(shù)傳入
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //消息的what
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //回調(diào)的Handler
    private static InternalHandler sHandler;
    //實(shí)現(xiàn)Callback册踩,存放參數(shù)
    private final WorkerRunnable<Params, Result> mWorker;
    //執(zhí)行的具體方法
    private final FutureTask<Result> mFuture;
    //記錄狀態(tài)
    private volatile Status mStatus = Status.PENDING;
    //高比發(fā)下保證原子性操作,記錄任務(wù)是否取消效拭,是否執(zhí)行
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    private static class SerialExecutor implements Executor {
        //上文已有暂吉,省略。缎患。慕的。
    }

    public enum Status {
        //等待執(zhí)行
        PENDING,
        //執(zhí)行中
        RUNNING,
        //結(jié)束
        FINISHED,
    }


    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //任務(wù)執(zhí)行
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //設(shè)置線程的優(yōu)先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //執(zhí)行方法
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //結(jié)果post
                    postResult(result);
                }
                return result;
            }
        };
        //相當(dāng)于修飾者模式
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                //異常的情況下,將結(jié)果返回挤渔,一般執(zhí)行不進(jìn)去
                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);
                }
            }
        };
    }

每處地方筆者幾乎都打上了注釋业稼,畢竟我就是那么的貼心,恩(°?°)?蚂蕴,其中有幾處細(xì)節(jié)
AsyncTask的線程池配置根據(jù)手機(jī)配置來的低散,緩沖隊(duì)列默認(rèn)是128,也就是說如果有一只4核的手機(jī)骡楼,那么根據(jù)如上API25的配置

  • CPU_COUNT=4
  • CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)) = 4
  • MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1 = 7

線程池中最大容納7個(gè)線程熔号,同時(shí)處理4個(gè)工作線程,滿出來的放到BlockingQueue中鸟整,也就是128的數(shù)量限制引镊,默認(rèn)還是串行執(zhí)行,如果工作線程過于密集,不僅會阻塞后續(xù)的線程弟头,還有可能拋出RejectedExecutionException異常吩抓,被線程池拒絕,至此赴恨,前文的AsyncTask的注釋提示適合短時(shí)間的任務(wù)也就揭開了面紗疹娶。
當(dāng)后臺任務(wù)完成會調(diào)用這個(gè)方法返回結(jié)果

 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 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;
            }
        }
    }
private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

通過InternalHandler來分發(fā)消息,然后對應(yīng)執(zhí)行mTask的相關(guān)回調(diào)方法

總結(jié)

綜上所訴伦连,AsyncTask驅(qū)動的其實(shí)是mFuture雨饺,本質(zhì)也是一個(gè)Runnable;進(jìn)度回調(diào)由我們自己調(diào)用publishProgress惑淳,工作線程限定

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

一遍源碼看下來额港,AsyncTask其實(shí)是將一個(gè)異步任務(wù)分配到線程池中來執(zhí)行,完成后通過Handler來分發(fā)的一個(gè)封裝的庫歧焦,由于默認(rèn)的執(zhí)行還是串行的移斩,雖然支持多線程并行,但是不太適合高頻的網(wǎng)絡(luò)請求绢馍,適用于一些輕量的IO操作向瓷。
Ps:由于使用過程中帶有一些坑,就偷懶不放調(diào)用流程圖了

參考:
AsyncTask和AsyncTaskCompat源碼解析
Android AsyncTask 源碼解析

凡勞苦擔(dān)重?fù)?dān)的人可以到我這里來痕貌,我就使你們得安息风罩。 (馬太福音 11:28 和合本)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糠排,一起剝皮案震驚了整個(gè)濱河市舵稠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌入宦,老刑警劉巖哺徊,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乾闰,居然都是意外死亡落追,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門涯肩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轿钠,“玉大人,你說我怎么就攤上這事病苗×贫猓” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵硫朦,是天一觀的道長贷腕。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么泽裳? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任瞒斩,我火速辦了婚禮,結(jié)果婚禮上涮总,老公的妹妹穿的比我還像新娘胸囱。我一直安慰自己,他們只是感情好妹卿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布旺矾。 她就那樣靜靜地躺著,像睡著了一般夺克。 火紅的嫁衣襯著肌膚如雪箕宙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天铺纽,我揣著相機(jī)與錄音柬帕,去河邊找鬼。 笑死狡门,一個(gè)胖子當(dāng)著我的面吹牛陷寝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播其馏,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凤跑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叛复?” 一聲冷哼從身側(cè)響起仔引,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褐奥,沒想到半個(gè)月后咖耘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撬码,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年儿倒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呜笑。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夫否,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叫胁,到底是詐尸還是另有隱情凰慈,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布曹抬,位于F島的核電站溉瓶,受9級特大地震影響急鳄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜堰酿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一疾宏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧触创,春花似錦坎藐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至抖韩,卻和暖如春蛀恩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背茂浮。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工双谆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人席揽。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓顽馋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幌羞。 傳聞我的和親對象是個(gè)殘疾皇子寸谜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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

  • 使用AsyncTask的一般步驟是: 定義一個(gè)類繼承自AsyncTask,實(shí)現(xiàn)抽象方法 new 一個(gè)AsyncTa...
    yk_looper閱讀 390評論 0 2
  • 前言 此前寫過一篇AsyncTask源碼分析的文章属桦,但寫的不是很好熊痴,最近看過了android7.0的AsyncTa...
    劉望舒閱讀 1,793評論 3 15
  • 基礎(chǔ)知識點(diǎn) 1.線程池Executor2.Future3.Callable4.中斷線程5.Handler6.枚舉7...
    Armstrong_Q閱讀 854評論 3 19
  • 簡介:AsyncTask是Android中用于異步操作的類。盡管現(xiàn)在已經(jīng)逐漸被強(qiáng)調(diào)舍棄,不過就源碼來說,Async...
    非墨Zero閱讀 257評論 1 0
  • 少年錦時(shí)又回到春末的五月凌晨的集市人不多小孩在門前唱著歌陽光它照暖了溪河柳絮乘著大風(fēng)吹樹影下的人想睡沉默的人從此刻...
    永遠(yuǎn)眼眶濕潤閱讀 225評論 0 0