AsyncTask

面試的時候在問線程通訊的時候很多人都會說到 AsyncTask陪汽,但是問到具體實現(xiàn)原理的時候發(fā)現(xiàn)很多人都不清楚昧谊,甚至有很多都是工作好幾年的,感覺也是挺有意思鸽斟。這里大概介紹一下吧(怎么感覺這篇文章完全是在湊字數(shù)拔创?)。

組成 & 目的

官網(wǎng)資料

先列一下官方資料:Android doc AsyncTask富蓄,源代碼位于 AsyncTask.java剩燥,七百行多一點的代碼(排除注釋后只有不到三百行,我寫的其實大多都是廢話立倍,看源碼最清晰了)灭红。

組成

AsyncTask 主要包含兩部分,靜態(tài)線程池 + handler口注。
靜態(tài)線程池:因為耗時操作肯定要在子線程中處理(不然就阻塞 UI 線程啦)变擒,靜態(tài)線程池就是用來干這個的。
Handler:負責子線程與 UI 線程的通訊寝志。

目的

這個類的設計目的就是可以讓開發(fā)者只關心業(yè)務娇斑,因為 Android 暫時跳不出線程這個框框策添,還沒有讓開發(fā)者完全不關心線程問題的能力(簡而言之就是還需要區(qū)分主線程與子線程),但是在開發(fā)者使用時又會產(chǎn)生關于線程處理的多余代碼毫缆,所以 google 搞了一個 AsyncTask唯竹,主要的目的就是讓用戶只聚焦自己業(yè)務,線程的事情交由 AsyncTask 內(nèi)部處理(只不過我是使用不慣這貨)苦丁。

使用方式

基本使用方式如下(實例化 AsyncTask浸颓,然后 execute):

AsyncTask<String, Integer, Exception> asyncTask = new AsyncTask<String, Integer, Exception>() {

  @Override
  protected void onPreExecute() {
    super.onPreExecute();
  }

  @Override
  protected Exception doInBackground(String... params) {
    return null;
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
  }

  @Override
  protected void onPostExecute(Exception e) {
  }

  @Override
  protected void onCancelled() {
  }
};
asyncTask.execute("");

這是 google 想讓開發(fā)者關心的地方,總結(jié)就是三個參數(shù)旺拉,五個回調(diào)猾愿。

三個參數(shù)

參見上邊的代碼,三個參數(shù)主要是指<Params, Progress, Result>账阻,對應上邊的代碼就是<String, Integer, Exception>(方便區(qū)分蒂秘,專門用了三個不同的類型),
Params 是傳入的參數(shù)淘太,即 doInBackground 中的 params姻僧。
Progress 傳出參數(shù),表示進度類型蒲牧,即 onProgressUpdate 中的 values撇贺。
Exception 傳出參數(shù),表示返回值類型冰抢,即 onPostExecute 中的 e松嘶。

五個回調(diào)

五個回調(diào)也挨個說一下:
onPreExecute(主線程):開始真正的任務(doInBackground)前會調(diào)用此回調(diào),讓開發(fā)者做一些準備工作挎扰。
doInBackground(子線程):這就是真正的耗時任務了翠订。
onProgressUpdate(主線程):更新進度,當用戶主動調(diào)用 publishProgress 后遵倦,AsyncTask 會通過 handler 通知到主線程尽超,然后主線程調(diào)用 onProgressUpdate 來通知開發(fā)者。
onPostExecute(主線程):耗時任務執(zhí)行完以后梧躺,執(zhí)行此回調(diào)似谁。
onCancelled(主線程):如果耗時任務被 cancel 的話,則調(diào)用此回調(diào)掠哥。

(發(fā)現(xiàn)寫起來比預想的更難以解釋巩踏,其實看源代碼比看我解釋清楚多了,源代碼只有兩百多行续搀,為啥就這么多人不看呢)

好吧塞琼,我們還是來直接看代碼吧(其實我是真不想這么干)。

源碼(android-25)

public abstract class AsyncTask<Params, Progress, Result> {
    
    // 顧名思義目代,CPU 的數(shù)量屈梁,主要是用來計算線程池默認包含線程的數(shù)量(最小值)
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    // 線程池包含線程的最小數(shù)量(當有新任務時嗤练,如果線程池中線程小于此值,則會創(chuàng)建新線程在讶,即使其他已有線程處于閑置狀態(tài))
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

    // 線程池的最大數(shù)量(超了就只能等著現(xiàn)有的某個線程執(zhí)行完了)
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

    // 已經(jīng)執(zhí)行任務結(jié)束的線程的續(xù)命時間(過了就被回收了)
    private static final int KEEP_ALIVE_SECONDS = 30;

    // 顧名思義煞抬,線程工廠,就是用來生成新線程的
    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());
        }
    };

    // 任務隊列
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);

    // ThreadPoolExecutor 的引用(關于 ThreadPoolExecutor构哺,就是線程池革答,這里不做過多介紹)
    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;
    }

    // 顧名思義,串行的執(zhí)行器曙强,至于其細節(jié)残拐,請看 SerialExecutor 的注釋
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    // 線程間通訊的標記,這個是用來標記傳遞的 Message 其中的內(nèi)容為結(jié)果
    private static final int MESSAGE_POST_RESULT = 0x1;

    // 線程間通訊的標記碟嘴,這個是用來標記傳遞的 Message 其中的內(nèi)容為進度
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    // 默認的執(zhí)行器溪食,默認值為串行,可通過 setDefaultExecutor 函數(shù)設置
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    // AsyncTask 中全局的娜扇、單例的错沃、主線程的 handler,這個 handler 主要用來子線程往主線程通訊使用
    private static InternalHandler sHandler;

    // 一個 WorkerRunnable 的實例(成員變量雀瓢,具體 WorkerRunnable 是干嘛的枢析,可以看 WorkerRunnable 的注釋)
    private final WorkerRunnable<Params, Result> mWorker;

    // 一個 FutureTask 的實例(成員變量,同樣刃麸,具體 FutureTask 是干嘛的請看 FutureTask 的注釋)
    private final FutureTask<Result> mFuture;

    // 當前 AsyncTask 的狀態(tài)
    private volatile Status mStatus = Status.PENDING;
    
    // 標記是否已經(jīng)被取消了
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    // 這也是比較坑的東西醒叁,串行控制器,所以現(xiàn)在 AsyncTask 沒有做其他設置的話泊业,默認是串行的
    // 但是這貨卻只負責將具體的 Runnable 包裝了一下把沼,然后再扔給 THREAD_POOL_EXECUTOR,并且 THREAD_POOL_EXECUTOR 里邊還有多個線程
    // 我是沒悟透這其中的邏輯脱吱,所以我感覺這么寫就是吃飽了撐的
    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);
            }
        }
    }

    // 三種狀態(tài)
    public enum Status {
        PENDING,
        RUNNING,
        FINISHED,
    }

    // 單例獲取 Handler(主線程的 Handler)
    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

    // 可以讓開發(fā)者主動調(diào)用來設置執(zhí)行器
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

    // 構(gòu)造函數(shù)智政,而且注釋明確寫著,這個函數(shù)必須要在主線程中調(diào)用
    // 構(gòu)造函數(shù)就干了兩件事箱蝠,初始化 mWorker 與 mFuture
    public AsyncTask() {
        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;
            }
        };

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

    // 如果 mWorker call 沒有被執(zhí)行的話,這通過此函數(shù)講結(jié)果返回給開發(fā)者
    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

    // 當任務執(zhí)行完成后垦垂,通過調(diào)用此函數(shù)將結(jié)果發(fā)送給主線程
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    // 獲取當前 AsyncTask 狀態(tài)
    public final Status getStatus() {
        return mStatus;
    }

    // 需要開發(fā)者必須實現(xiàn)的任務執(zhí)行函數(shù)宦搬,此函數(shù)在工作線程執(zhí)行
    @WorkerThread
    protected abstract Result doInBackground(Params... params);

    // 在執(zhí)行 doInBackground 前,給用戶一個回調(diào)劫拗,讓開發(fā)者做一些準備工作间校,此函數(shù)在主線程執(zhí)行
    @MainThread
    protected void onPreExecute() {
    }

    // 回調(diào)接口,可以將 doInBackground 返回的結(jié)果通過此回調(diào)返回給開發(fā)者(其實主要還是一個跨線程的問題)
    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onPostExecute(Result result) {
    }

    // 回調(diào)接口页慷,將進度通過此回調(diào)返回給開發(fā)者憔足,當開發(fā)者調(diào)用 publishProgress 才會執(zhí)行此回調(diào)(主要也還是因為跨線程的問題胁附,讓 sHandler 來將運行在子線程的數(shù)據(jù)傳送到主線程)
    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

    // 回調(diào)接口,當開發(fā)者主動調(diào)用 cancel 時會通過此回調(diào)通知開發(fā)者
    @SuppressWarnings({"UnusedParameters"})
    @MainThread
    protected void onCancelled(Result result) {
        onCancelled();
    }    
    
    // 回調(diào)接口滓彰,當開發(fā)者主動調(diào)用 cancel 時會通過此回調(diào)通知開發(fā)者
    @MainThread
    protected void onCancelled() {
    }

    // 獲取當前 AsyncTask 是否被 cancel
    public final boolean isCancelled() {
        return mCancelled.get();
    }

    // 主動 cancel 當前執(zhí)行的任務
    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

    // 獲取執(zhí)行結(jié)果控妻,注意這是一個同步函數(shù),只有運算結(jié)束后揭绑,此函數(shù)才會繼續(xù)執(zhí)行
    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    // 獲取執(zhí)行結(jié)果弓候,這也是同步函數(shù),當設置的時間后如果執(zhí)行仍未結(jié)束他匪,則通過 TimeoutException 來告訴開發(fā)者
    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

    // 開發(fā)者調(diào)用的執(zhí)行函數(shù)
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    // 開發(fā)者調(diào)用的執(zhí)行函數(shù)
    @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;
    }

    // 開發(fā)者調(diào)用的執(zhí)行函數(shù)菇存,將 runnable 傳給 sDefaultExecutor
    @MainThread
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

    // 更新進度函數(shù),會通過 InternalHandler 把進度傳遞給主線程
    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

    // 結(jié)束的時候調(diào)用此函數(shù)邦蜜,然后調(diào)用回調(diào)通知開發(fā)者
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

    // 主線程的 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:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

    // 雖然這貨不是一個 Runnable,但是其實就是把一些邏輯封裝到一起悼沈,可以讓其他線程直接調(diào)用一個函數(shù)就可以執(zhí)行了
    // 具體肯以看 AsyncTask 構(gòu)造函數(shù)中 WorkerRunnable 的實例化代碼毕籽,主要就是把 doInBackground 等函數(shù)包裝了一下
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

    @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

注意

需要注意的是 AsyncTask 不同版本的實現(xiàn)是不同的,這也是比較坑的地方井辆。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末关筒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杯缺,更是在濱河造成了極大的恐慌蒸播,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍肆,死亡現(xiàn)場離奇詭異袍榆,居然都是意外死亡,警方通過查閱死者的電腦和手機塘揣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門包雀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亲铡,你說我怎么就攤上這事才写。” “怎么了奖蔓?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵赞草,是天一觀的道長。 經(jīng)常有香客問我吆鹤,道長厨疙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任疑务,我火速辦了婚禮沾凄,結(jié)果婚禮上梗醇,老公的妹妹穿的比我還像新娘。我一直安慰自己撒蟀,他們只是感情好叙谨,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牙肝,像睡著了一般唉俗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上配椭,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天虫溜,我揣著相機與錄音,去河邊找鬼股缸。 笑死衡楞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的敦姻。 我是一名探鬼主播瘾境,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼镰惦!你這毒婦竟也來了迷守?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤旺入,失蹤者是張志新(化名)和其女友劉穎兑凿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茵瘾,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡礼华,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拗秘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圣絮。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雕旨,靈堂內(nèi)的尸體忽然破棺而出扮匠,到底是詐尸還是另有隱情,我是刑警寧澤奸腺,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布餐禁,位于F島的核電站,受9級特大地震影響突照,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜氧吐,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一讹蘑、第九天 我趴在偏房一處隱蔽的房頂上張望末盔。 院中可真熱鬧,春花似錦座慰、人聲如沸陨舱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽游盲。三九已至,卻和暖如春蛮粮,著一層夾襖步出監(jiān)牢的瞬間益缎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工然想, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莺奔,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓变泄,卻偏偏與公主長得像令哟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妨蛹,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

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