Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AsyncTask 封裝初步一

Android AsyncTask 封裝初步一

編輯:關於Android編程

AsyncTask基本使用

先從最熟悉的Task使用開始說起,給出LastDaysTask作為實例如下

@SuppressLint("NewApi")
private class LastDaysTask extends AsyncTask{

    @Override
    protected void onPreExecute()
    {
        //TODO UI線程或者主線程
    }

    @Override
    protected Boolean doInBackground(Void... params)
    {
        //TODO 工作線程或者子線程
        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values)
    {
        //TODO UI線程或者主線程
    }

    @Override
    protected void onPostExecute(Boolean aBoolean)
    {
        //TODO UI線程或者主線程
    }
}

這裡我們把AsyncTask的第一個泛型參數指定為Void,表示在執行AsyncTask的時候不需要傳入參數給後台任務。第二個泛型參數指定為Integer,表示使用整型數據來作為進度顯示單位。第三個泛型參數指定為Boolean,則表示使用布爾型數據來反饋執行結果。

接下來需要重寫其中的四個方法,分別是:

onPreExecute() onProgressUpdate(Integer… values) doInBackground(Void… params) onPostExecute(Boolean aBoolean)

對上面這四個方法簡短說明如下:

onPreExecute()
這個方法會在後台任務開始執行之間調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。 onProgressUpdate(Progress…)
當在後台任務中調用了publishProgress(Progress…)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。在這個方法中可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。 doInBackground(Params…)
這個方法中的所有代碼都會在子線程中運行,我們應該在這裡去處理所有的耗時任務。任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress…)方法來完成。 onPostExecute(Result)
當後台任務執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。

使用異步任務獲取數據

@Override
public void onCreate()
{
    super.onCreate();

    LastDaysTask task = new LastDaysTask();
    task.execute();
}

這樣就簡單的完成了子線程和UI線程之間的通信。

AsyncTask主要源碼

看完上面如何使用異步任務獲取遠程數據,我們以task.execute();作為切入點,深入源碼看看究竟。

AsyncTask

public final AsyncTask execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
    if (mStatus != Status.PENDING) {
        /*
        如果同一個task被啟動兩次,就會拋出下面兩種異常,所以如果task作為成員變量的時候,一定要判斷當前task是否已經在運行,否則不可以重復execute.除非先把該task取消以後才可以繼續execute.
        */
        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;

    /*
    該方法在UI線程,獲取遠程數據之前,UI需要更新狀態可以重寫該方法
    */
    onPreExecute();

    /*
    這裡有兩個成員變量mWorker和mFuture很重要,這兩個變量都是final型,在構造器中初始化,另外還有一個sDefaultExecutor多線程執行器,然後返回結果當前實例this。
    */
    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

成員變量mWorker和mFuture

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private final WorkerRunnable mWorker;
private final FutureTask mFuture;

/*
- 構造器,初始化兩個重要的成員變量mWorker和mFuture,在mWorker中我們看到了熟悉的doInBackground()方法,很明顯這個是放在子線程中執行的,另外WorkerRunnable這個類簡單的實現了Callable接口,新增成員變量Params數組。
*/

public AsyncTask() {
    mWorker = new WorkerRunnable() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

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

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

private static abstract class WorkerRunnable implements Callable {
    Params[] mParams;
}

sDefaultExecutor默認分發器

/*
- 默認分發器,SerialExecutor根據名稱大概可以猜到這是個串行分發器,然後前面使用異步任務的時候調用了execute()方法,我們一路跟蹤到這裡,最後進入到上面代碼L29 exec.execute(mFuture), 而這個exec就是這個串行分發器,然後進入串行分發器的execute(final Runnable r)方法中.mTasks是個循環隊列,offer加入隊列尾,poll拿出隊列頭,剛開始進入execute()方法,將mFuture加入循環隊列中,然後判斷mActive(就是加入進來的mFuture)是否為空,第一次進來當然為空,所以執行 scheduleNext()方法,取出剛加入的mFuture放入線程池中去執行,接下來看線程池的定義
*/
private static class SerialExecutor implements Executor {
    final ArrayDeque mTasks = new ArrayDeque();
    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();
        }
    }

    /*
    進入線程池execute方法以後執行Runnable中的run方法,即上面L10處,然後執行出入參數為Runnable的run方法,最後在finally塊中執行下面一個隊列中的Runnable的run方法,依次取出隊列。我們看到無論隊列中前面一個Runnable執行的如何都會去取下面一個Runnable實例去執行,所以不用擔心諸如異常導致的阻塞行為。
    */
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

THREAD_POOL_EXECUTOR線程池


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;

/*
線程工廠生產出來的線程做了名稱標記,所以我們看Log經常會看到"AsyncTask #" + mCount.getAndIncrement()比如“AsyncTask #1”等等字樣。而線程池的核心線程數和最大線程數都是通過Runtime動態獲取和指定的。
*/
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());
    }
};
/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

經過上面的源碼分析,我們小結一下:
AsyncTask中有個默認串行分發器,它負責將Runnable實例加入循環隊列中,然後逐一取出交由線程池去執行,根據Runtime決定線程池的大小和規格,最小核心線程數是2,線程名都是以”AsyncTask #”開頭,所以當我們在一個生命周期中提交多個異步任務,一般是逐步加入線程池中去執行,這種實現就像是模擬單一的線程池一樣,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執行,其余的均處於等待狀態。我們知道對於這個默認的串行分發器,在Android3.0之前是沒有的,那麼為什麼加入這個默認的串行分發器呢?原因就是AsyncTask在Android3.0以後提供了更大的靈活性,這裡僅僅是提供了默認的串行分發器,如果你感覺太過保守,你可以不使用這個默認的串行分發器,而是直接這樣。

Executor exec = new ThreadPoolExecutor(15, 200, 10,  
        TimeUnit.SECONDS, new LinkedBlockingQueue());  
new LastDaysTask().executeOnExecutor(exec); 

這樣就可以使用我們自定義的一個Executor來執行任務,而不是使用SerialExecutor。上述代碼的效果允許在同一時刻有15個任務正在執行,並且最多能夠存儲200個任務。

PS:
小結一下AsyncTask的任務是並行還是串行執行?

在Android 1.5剛引入的時候,AsyncTask的execute是串行執行的; 到了Android 1.6直到Android 2.3.2,又被修改為並行執行了,這個執行任務的線程池就是THREAD_POOL_EXECUTOR,因此在一個進程內,所有的AsyncTask都是並行執行的; 但是在Android 3.0以後,如果你使用execute函數直接執行AsyncTask,那麼這些任務是串行執行的;

AsyncTask其他源碼剖析

前面我們分析了大概脈絡,這裡我們進一步分析子線程和UI線程的通信部分。

提交給線程池執行execute()方法,在sDefaultExecutor默認分發器中的L12行,參數Runnable中的r傳參是mFuture實例,所以直接進入FutureTask的run()方法中.

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                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);
    }
}

重點看上面的L12行,調用Callable的call()方法,而這個Callable就是我們構造器中的mWoker,所以還是回到AsyncTask構造器。

public AsyncTask() {
/*
進入call()方法中,設置task是否被喚起,線程優先級以及我們熟悉的doInBackground方法,並將結果postResult到UI線程中,接著繼續跟postResult方法。
*/
    mWorker = new WorkerRunnable() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            return postResult(doInBackground(mParams));
        }
    };
}

postResult()方法,該方法完成線程間通信。

private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    /*
    這裡看到了熟悉的Message,獲取message以後將消息一並發出去,接受者key就是MESSAGE_POST_RESULT,所以我們跟蹤這個key,看看處理這如何處理。
    */
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult(this, result));
    message.sendToTarget();
    return result;
}

MESSAGE_POST_RESULT

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

L9 調用finish()方法

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

上面的finish方法根據Task的狀態調用不同的狀態方法,然後置位狀態。這裡看到了熟悉的onPostExecute()方法又一次回到了UI線程。我們注意到,在剛才InternalHandler的handleMessage()方法裡,還有一種MESSAGE_POST_PROGRESS的消息類型,這種消息是用於當前進度的,調用的正是onProgressUpdate()方法,那麼什麼時候才會發出這樣一條消息呢?相信你已經猜到了,查看publishProgress()方法的源碼,如下所示:

protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult
(this, values)).sendToTarget(); } }

最後我們還有一個構造器中的mFuture還沒有分析,因為只有一個done方法,所以有必要一起來看一下FutureTask中的done方法

 /**
 * Protected method invoked when this task transitions to state
 * {@code isDone} (whether normally or via cancellation). The
 * default implementation does nothing.  Subclasses may override
 * this method to invoke completion callbacks or perform
 * bookkeeping. Note that you can query status inside the
 * implementation of this method to determine whether this task
 * has been cancelled.
 */
protected void done() { }

看注釋應該清楚,這裡需要子類去重寫,需要判斷狀態做出相應的處理即可,解析來看看AsyncTask構造器中的done方法

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

主要的方法是postResultIfNotInvoked(get())然後把結果傳過去

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}

如果task沒有被喚起的話,或者說沒有執行call()方法的話,wasTaskInvoked標志位為false,還是通過postResult方法將數據傳給UI線程,到此分析結束。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved