Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AsyncTask介紹

AsyncTask介紹

編輯:關於Android編程

0x01.簡介

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by thejava.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask. 谷歌官網的介紹,大意是用來輔助Thread和Handler的類,不能作為一個獨立線程框架。AsyncTask用來做短時間的任務,如果你想做長時間的任務,最好用別的框架。   其實,AsyncTask就是用於更加簡便的執行一個耗時任務,在主線程更新UI。

0X02.簡單使用

google提供的AsyncTask的簡單示例。
 private class DownloadFilesTask extends AsyncTask {
     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");
     }
 }
在幾個我們可以復寫的方法中,除了doInBackgroud方法別的都是執行在主線程中的。 當我們使用一個AsyncTask的時候我們必須指定3個泛型類型和實現doInBackground()函數。
public abstract class AsyncTask
這是AnsyncTask需要定義的3個泛型數據。 第一個Params是我們需要指定重寫的doInBackgroud的參數
protected abstract Result doInBackground(Params... params);
第二個progress是用於指定更新進度方法傳入的參數
protected void onProgressUpdate(Progress... values) {
}
第三個Result是用於我們指定doInBackgroun返回值和onPostExecute方法的參數
protected void onPostExecute(Result result) {
}
必須要實現的abstract方法doInBackgroud,就是我們需要在後台做的任務。 整個執行流程 \\  
上面就是AsyncTask的一般流程(AsyncTask提供的cancel()方法,當我們調用這個方法的時候,流程就不是這個樣子了),execut是需要我們自己調用以開始執行。在我們可以的復寫的方法中,只有doInBackgroud是在額外線程中執行的。我們可以在doInBackgroud中調用publishProgress方法這樣主線程會去調用OnProgressUpdate方法。在開啟線程正常完成任務後會給主線程發送消息,調用OnPostExecute方法。
\ 上面是官網提供的各種API,並且具有cancel, get等方法(是不是很熟悉,AsyncTask中就是包含一個FutureTask)。executeOnExecutor執行方法中允許我們自己定義Executor,也就是說我們可以用線程池去Execute去執行也可以用Thread。

0x03.源碼分析

Execute()

我們先從execute開始看
    @MainThread
    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);//調用另外一個execute方法,並且傳入默認的Executor
    }
    public final AsyncTask 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中執行會自動調用executeOnExecutor方法,並且傳入一個默認的sDefaultExecutor, 等下會說這個sDefaultExecutor是干什麼的(接下來涉及到Executor接口和ThreadPoolExecutor, 可以看下簡單的用法再食用- -;),一開始會先對實例是否執行做一個檢查,如果沒問題的話就開始執行。改變狀態,調用回調。mWorker是一個實現了Callable接口並且可以保存傳入參數的實例,等回詳細介紹,執行mFuture,mFuture其實就是封裝了mWorker的一個FutureTask。  

默認執行Runnable的Executor

 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();//AsyncTask靜態,靜態,靜態的變量
 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//volatile保證多線程下數據的可見性,但是注意無法保證原子性(不理解也沒有關系)
 private static class SerialExecutor implements Executor {
     final ArrayDeque mTasks = new ArrayDeque();//雙向隊列
     Runnable mActive;
     public synchronized void execute(final Runnable r) {//這個方法是線程安全的
     	mTasks.offer(new Runnable() {//將傳入的Runnable
             public void run() {
                 try {
                     r.run();//執行異步任務
                 } finally {
                     scheduleNext();//當一個異步任務執行完成之後必定會調用這個方法,不管是否正常結束
                 }
             }
         });
         if (mActive == null) {//在當前無異步執行的時候調用
             scheduleNext();
         }
     }

     protected synchronized void scheduleNext() {//線程安全
              THREAD_POOL_EXECUTOR.execute(mActive);
         }
     }
 }
sDefaultExecutor是靜態內部類SerialExecutor的一個靜態實例。這個變量是用來執行所有異步操作,我們看execute將傳入的Runnable封裝成另外一個Runnable,以便在執行run方法後執行scheduleNext()方法,並且入隊,如果沒有異步操作的話就會調用scheduleNext()方法。scheduleNext()是調用mTask中取出一個元素,放入THREAD_POOL_EXECUTOR來執行,THREAD_POOL_EXECUTOR其實是一個是一個ThreadPoolExecutor的實例,每次執行異步任務都是放入一個線程池中執行。從上面的代碼可以看出,每次線程池中只有一個線程在運行,如果當前有線程在運行的話,那麼只會將傳入的Runnable入隊,等待執行完畢後調用,並且所有的異步都是由duque來保存的所以所有任務執行順序按 。 也就是說在默認的Executor中執行異步操作,因為sDefaultExecutor是一個靜態類,所有的異步操作都是一個一個進行的,而且先後有序,我猜想這就是為什麼google建議你運行短時間的原因,如果一個異步花太多時間執行,那麼必將導致別的異步任務也將很長時間執行完成,降低體驗。
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//獲取虛擬機的處理器核心數
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));//線程池的核心線程數
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//線程池最大線程數
    private static final int KEEP_ALIVE_SECONDS = 30;//線程池超過核心線程
    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;
    }
    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());//為每個線程起名
        }
    };
上面是對線程池各種參數設置,  

mWorker和mFuture

 
    private final WorkerRunnable mWorker;
    private final FutureTask mFuture;
    public AsyncTask() {
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);//標記任務被調用
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);//調用回調
                Binder.flushPendingCommands();//
                return postResult(result);//調用回調
            }
        };

        mFuture = new FutureTask(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);
                }
            }
        };
    }
    private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
    }

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

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
可以看出mWorker是一個實現了Callable接口和可以保存mParams的一個實例。調用doInBackgroud和postResult,返回結果。mFuture將mWorker封裝成一個FutureTask對象,那麼mWorker就可以被一個實現了Executor接口的類所調用了。並且在mFuture中保證如果沒有執行Task的話調用PostResultIFNotInvoked,這個方法如果發現task沒有被執行,那麼回去調用postResult(result),用於完成onPostExecute的回調。 到現在我們可以整理下,我們調用execute(params),在調用execute線程執行onPreExecute()用默認的Executor異步執行封裝了mWorker的Future,在mWorker中執行了doInBackgroud並且將結果傳給postResult(result)。大致的流程理清楚了。那麼postResult(result)中有什麼?

postResult(result)

 
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }

    private static Handler getHandler() {//根據單例模式獲取
        synchronized (AsyncTask.class) {//對類上鎖,以防多次創建
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

    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;
            }
        }
    }
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {//發送低啊用用onprogressUpdate的回調
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult
在postResult中會直接生成一個綁定主線程的Message並且投遞給主線程,讓主線程調用finish,根據是否被取消了,調用具體的回調。publishProgress也是一樣通過向主線程投遞消息,讓主線程調用onProgressUpdate。  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved