Android源碼分析—帶你認識不同的AsyncTask

轉載請註明出處:http://blog.csdn.net/singwhatiwanna/article/details/17596225java

前言

什麼是AsyncTask,相信搞過android開發的朋友們都不陌生。AsyncTask內部封裝了Thread和Handler,可讓咱們在後臺進行計算而且把計算的結果及時更新到UI上,而這些正是Thread+Handler所作的事情,沒錯,AsyncTask的做用就是簡化Thread+Handler,讓咱們可以經過更少的代碼來完成同樣的功能,這裏,我要說明的是:AsyncTask只是簡化Thread+Handler而不是替代,實際上它也替代不了。同時,AsyncTask從最開始到如今已經通過了幾回代碼修改,任務的執行邏輯慢慢地發生了改變,並非你們所想象的那樣:AsyncTask是徹底並行執行的就像多個線程同樣,其實不是的,因此用AsyncTask的時候仍是要注意,下面會一一說明。另外本文主要是分析AsyncTask的源代碼以及使用時候的一些注意事項,若是你還不熟悉AsyncTask,請先閱讀android之AsyncTask 來了解其基本用法。android

這裏先給出AsyncTask的一個例子:express

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

使用AsyncTask的規則

  • AsyncTask的類必須在UI線程加載(從4.1開始系統會幫咱們自動完成)
  • AsyncTask對象必須在UI線程建立
  • execute方法必須在UI線程調用
  • 不要在你的程序中去直接調用onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法
  • 一個AsyncTask對象只能執行一次,即只能調用一次execute方法,不然會報運行時異常
  • AsyncTask不是被設計爲處理耗時操做的,耗時上限爲幾秒鐘,若是要作長耗時操做,強烈建議你使用Executor,ThreadPoolExecutor以及FutureTask
  • 在1.6以前,AsyncTask是串行執行任務的,1.6的時候AsyncTask開始採用線程池裏處理並行任務,可是從3.0開始,爲了不AsyncTask所帶來的併發錯誤,AsyncTask又採用一個線程來串行執行任務

AsyncTask究竟是串行仍是並行?

給你們作一下實驗,請看以下實驗代碼:代碼很簡單,就是點擊按鈕的時候同時執行5個AsyncTask,每一個AsyncTask休眠3s,同時把每一個AsyncTask執行結束的時間打印出來,這樣咱們就能觀察出究竟是串行執行仍是並行執行。apache

@Override
    public void onClick(View v) {
        if (v == mButton) {
            new MyAsyncTask("AsyncTask#1").execute("");
            new MyAsyncTask("AsyncTask#2").execute("");
            new MyAsyncTask("AsyncTask#3").execute("");
            new MyAsyncTask("AsyncTask#4").execute("");
            new MyAsyncTask("AsyncTask#5").execute("");
        }

    }

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

        private String mName = "AsyncTask";

        public MyAsyncTask(String name) {
            super();
            mName = name;
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return mName;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e(TAG, result + "execute finish at " + df.format(new Date()));
        }
    }
我找了2個手機,系統分別是4.1.1和2.3.3,按照我前面的描述,AsyncTask在4.1.1應該是串行的,在2.3.3應該是並行的,究竟是不是這樣呢?請看Log

Android 4.1.1上執行:從下面Log能夠看出,5個AsyncTask共耗時15s且時間間隔爲3s,很顯然是串行執行的併發


Android 2.3.3上執行:從下面Log能夠看出,5個AsyncTask的結束時間是同樣的,很顯然是並行執行app


結論:從上面的兩個Log能夠看出,我前面的描述是徹底正確的。下面請看源碼,讓咱們去了解下其中的原理。less

源碼分析

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class AsyncTask<Params, Progress, Result> {
    private static final String LOG_TAG = "AsyncTask";

	//獲取當前的cpu核心數
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
	//線程池核心容量
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
	//線程池最大容量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
	//過剩的空閒線程的存活時間
    private static final int KEEP_ALIVE = 1;
	//ThreadFactory 線程工廠,經過工廠方法newThread來獲取新線程
    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());
        }
    };
	//靜態阻塞式隊列,用來存放待執行的任務,初始容量:128個
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * 靜態併發線程池,能夠用來並行執行任務,儘管從3.0開始,AsyncTask默認是串行執行任務
	 * 可是咱們仍然能構造出並行的AsyncTask
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 靜態串行任務執行器,其內部實現了串行控制,
	 * 循環的取出一個個任務交給上述的併發線程池去執行
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
	//消息類型:發送結果
    private static final int MESSAGE_POST_RESULT = 0x1;
	//消息類型:更新進度
    private static final int MESSAGE_POST_PROGRESS = 0x2;
	/**靜態Handler,用來發送上述兩種通知,採用UI線程的Looper來處理消息
	 * 這就是爲何AsyncTask必須在UI線程調用,由於子線程
	 * 默認沒有Looper沒法建立下面的Handler,程序會直接Crash
	 */
    private static final InternalHandler sHandler = new InternalHandler();
	//默認任務執行器,被賦值爲串行任務執行器,就是它,AsyncTask變成串行的了
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
	//以下兩個變量咱們先不要深究,不影響咱們對總體邏輯的理解
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
	//任務的狀態 默認爲掛起,即等待執行,其類型標識爲易變的(volatile)
    private volatile Status mStatus = Status.PENDING;
    //原子布爾型,支持高併發訪問,標識任務是否被取消
    private final AtomicBoolean mCancelled = new AtomicBoolean();
	//原子布爾型,支持高併發訪問,標識任務是否被執行過
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

	/*串行執行器的實現,咱們要好好看看,它是怎麼把並行轉爲串行的
	 *目前咱們須要知道,asyncTask.execute(Params ...)實際上會調用
	 *SerialExecutor的execute方法,這一點後面再說明。也就是說:當你的asyncTask執行的時候,
	 *首先你的task會被加入到任務隊列,而後排隊,一個個執行
	 */
    private static class SerialExecutor implements Executor {
		//線性雙向隊列,用來存儲全部的AsyncTask任務
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
		//當前正在執行的AsyncTask任務
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
			//將新的AsyncTask任務加入到雙向隊列中
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
						//執行AsyncTask任務
                        r.run();
                    } finally {
						//當前AsyncTask任務執行完畢後,進行下一輪執行,若是還有未執行任務的話
						//這一點很明顯體現了AsyncTask是串行執行任務的,老是一個任務執行完畢纔會執行下一個任務
                        scheduleNext();
                    }
                }
            });
			//若是當前沒有任務在執行,直接進入執行邏輯
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
			//從任務隊列中取出隊列頭部的任務,若是有就交給併發線程池去執行
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

    /**
     * 任務的三種狀態
     */
    public enum Status {
        /**
         * 任務等待執行
         */
        PENDING,
        /**
         * 任務正在執行
         */
        RUNNING,
        /**
         * 任務已經執行結束
         */
        FINISHED,
    }

    /** 隱藏API:在UI線程中調用,用來初始化Handler */
    public static void init() {
        sHandler.getLooper();
    }

    /** 隱藏API:爲AsyncTask設置默認執行器 */
    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
	//doInBackground執行完畢,發送消息
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    /**
     * 返回任務的狀態
     */
    public final Status getStatus() {
        return mStatus;
    }

    /**
	 * 這個方法是咱們必需要重寫的,用來作後臺計算
	 * 所在線程:後臺線程
     */
    protected abstract Result doInBackground(Params... params);

    /**
	 * 在doInBackground以前調用,用來作初始化工做
	 * 所在線程:UI線程
     */
    protected void onPreExecute() {
    }

    /**
	 * 在doInBackground以後調用,用來接受後臺計算結果更新UI
	 * 所在線程:UI線程
     */
    protected void onPostExecute(Result result) {
    }

    /**
     * Runs on the UI thread after {@link #publishProgress} is invoked.
     /**
	 * 在publishProgress以後調用,用來更新計算進度
	 * 所在線程:UI線程
     */
    protected void onProgressUpdate(Progress... values) {
    }

     /**
	 * cancel被調用而且doInBackground執行結束,會調用onCancelled,表示任務被取消
	 * 這個時候onPostExecute不會再被調用,兩者是互斥的,分別表示任務取消和任務執行完成
	 * 所在線程:UI線程
     */
    @SuppressWarnings({"UnusedParameters"})
    protected void onCancelled(Result result) {
        onCancelled();
    }    
    
    protected void onCancelled() {
    }

    public final boolean isCancelled() {
        return mCancelled.get();
    }

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

    /**
     * 這個方法如何執行和系統版本有關,在AsyncTask的使用規則裏已經說明,若是你真的想使用並行AsyncTask,
	 * 也是能夠的,只要稍做修改
	 * 必須在UI線程調用此方法
     */
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
		//串行執行
        return executeOnExecutor(sDefaultExecutor, params);
		//若是咱們想並行執行,這樣改就好了,固然這個方法咱們無法改
		//return executeOnExecutor(THREAD_POOL_EXECUTOR, params);
    }

    /**
     * 經過這個方法咱們能夠自定義AsyncTask的執行方式,串行or並行,甚至能夠採用本身的Executor
	 * 爲了實現並行,咱們能夠在外部這麼用AsyncTask:
	 * asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params);
	 * 必須在UI線程調用此方法
     */
    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會最早執行
        onPreExecute();

        mWorker.mParams = params;
		//而後後臺計算#doInBackground才真正開始
        exec.execute(mFuture);
		//接着會有#onProgressUpdate被調用,最後是#onPostExecute

        return this;
    }

    /**
     * 這是AsyncTask提供的一個靜態方法,方便咱們直接執行一個runnable
     */
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

    /**
	 * 打印後臺計算進度,onProgressUpdate會被調用
     */
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

	//任務結束的時候會進行判斷,若是任務沒有被取消,則onPostExecute會被調用
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

	//AsyncTask內部Handler,用來發送後臺計算進度更新消息和計算完成消息
    private static class InternalHandler extends Handler {
        @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 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在3.0以上的系統中並行起來

經過上面的源碼分析,我已經給出了在3.0以上系統中讓AsyncTask並行執行的方法,如今,讓咱們來試一試,代碼仍是以前採用的測試代碼,咱們要稍做修改,調用AsyncTask的executeOnExecutor方法而不是execute,請看:async

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public void onClick(View v) {
        if (v == mButton) {
            new MyAsyncTask("AsyncTask#1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
            new MyAsyncTask("AsyncTask#2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
            new MyAsyncTask("AsyncTask#3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
            new MyAsyncTask("AsyncTask#4").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
            new MyAsyncTask("AsyncTask#5").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
        }

    }

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

        private String mName = "AsyncTask";

        public MyAsyncTask(String name) {
            super();
            mName = name;
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return mName;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.e(TAG, result + "execute finish at " + df.format(new Date()));
        }
    }
下面是系統爲4.1.1手機打印出的Log:很顯然,咱們的目的達到了,成功的讓AsyncTask在4.1.1的手機上並行起來了,很高興吧!但願這篇文章對你有用。

相關文章
相關標籤/搜索