編輯:關於Android編程
AsyncTask的一個典型的應用場景是:後台下載文件,並實時跟新下載進度。它既能使得耗時的事情在後台線程中執行,又能和主線程通信,告訴主線程更新UI。同時,AsyncTask內部使用了線程池來執行後台任務,因此它能處理多任務請求。那麼它的內部是怎麼實現的呢?
在閱讀源碼之前,我們還是看一下AsyncTask的使用步驟:
這部分參考了AsyncTask的基本用法 這篇博客。
2.1onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些准備工作,如在界面上顯示一個進度條。
2.2doInBackground(Params…), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
2.3onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
2.4onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後台的計算結果將通過該方法傳遞到UI thread.
使用executeOnExecutor後者execute方法。
同時,我們還需要注意一下幾點:
A)Task的實例必須在UI thread中創建
B) execute方法必須在UI thread中調用
C) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
D) 該task只能被執行一次,否則多次調用時將會出現異常
AsyncTask的源碼不多,因此分析比較容易。我之所以要分析這個類的源碼是因為我遇到了這樣的困惑:既然AsyncTask使用線程池來處理多任務請求,那麼我提交多個任務是不是應該這樣呢?
//1 創建一個AsyncTask 的實例 AsyncTask asyncTask = new XXAsyncTask() //2 提交多個任務 asyncTask.execute(1); asyncTask.execute(2); asyncTask.execute(3); asyncTask.execute(4); asyncTask.execute(5);
很不幸的是,程序立刻就掛掉了,問什麼呢?我想的,既然AsyncTask內部有個線程池,那麼我不斷給它提交任務不就可以了嗎?而事實證明這樣不行,那麼正確的姿勢是怎麼樣的呢?
比如要提交10個任務:
for(int i=0;i<10;i++){ //1 創建一個AsyncTask 的實例 AsyncTask asyncTask = new XXAsyncTask() //2 提交多個任務 asyncTask.execute(i); }
也就是說要不斷的new AsyncTask 的實例,這是什麼情況呢?難道不是每個AsyncTask都有一個線程池嗎?還真不是的。
打開源碼一看,立刻明白了,AsyncTask使用的是單例模式。
/** * 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); /** * An {@link Executor} that executes tasks one at a time in serial * order. This serialization is global to a particular process. */ public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
AsyncTask構造了兩個靜態的線程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR 。因為它們是靜態的,因此所有的AsyncTask對象都共享這兩個線程池。也就是說,我已開始的理解就是錯的,我以為一個AsyncTask內部有一個線程池,其實不然,而是所有的對象共享這兩個線程池,其中SERIAL_EXECUTOR 是默認使用的線程池,當然我們可以選擇使用THREAD_POOL_EXECUTOR作為線程池的,主要用到executeOnExecutor方法,比如:
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,1);
理解了這點後,我們看看AsyncTask的具體內部實現。
當我們創建好AsyncTask實例後,采用默認的線程池的情況下,我們會執行excute方法,這個方法如下:
@MainThread public final AsyncTaskexecute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
調用executeOnExecutor方法進一步處理,注意傳給executeOnExecutor方法的參數sDefaultExecutor定義如下:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
也就是說默認的線程池是SERIAL_EXECUTOR了。
然後我們看一下executeOnExecutor是如何進一步處理的:
@MainThread public final AsyncTaskexecuteOnExecutor(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; }
該方法中出現的Status定義如下:
public enum Status { /** * Indicates that the task has not been executed yet. */ PENDING, /** * Indicates that the task is running. */ RUNNING, /** * Indicates that {@link AsyncTask#onPostExecute} has finished. */ FINISHED, }
Status中只定義了三種狀態,因此,asyncTask的狀態如果不是Status.PENDING,就會拋出異常。這是為什麼我們向下面這樣使用程序會掛掉:
//1 創建一個AsyncTask 的實例 AsyncTask asyncTask = new XXAsyncTask() //2 提交多個任務 asyncTask.execute(1); asyncTask.execute(2); asyncTask.execute(3);
第一次調用execute的時候,asyncTask的狀態由Status.PENDING轉為Status.RUNNING,第二次調用的是有,asyncTask的狀態還是Status.RUNNING,因此程序就會掛掉了。
接下來會調用onPreExecute()方法,也就是說這個方法是在UI線程中調用的,我們完全可以在其中更新UI。做一個初始化工作,這個時候,我們提交的任務還沒有執行。繼續往下看。
接下來調用了:
exec.execute(mFuture);
exec就是excute方法中傳入的sDefaultExecutor ,它默認初始化為SERIAL_EXECUTOR,SERIAL_EXECUTOR的定義如下:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static class SerialExecutor implements Executor { final ArrayDequemTasks = 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(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
exec.excute方法也就是這裡的execute方法了。這裡構造了一個Runnable的實例並把它添加到mTasks 中,第一次執行到這裡mActive 肯定為null,因此會調用scheduleNext方法,這個方法使用 mTasks.poll()去除之前添加的Runable,然後執行THREAD_POOL_EXECUTOR.execute(mActive),從而把Runable添加到線程池中。
Runnable中調用了傳入的Runnable的run方法,這個Runable就是mFuture,mFuture定義如下:
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); } } };
mFuture 是一個FutureTask的實例,並且這個實例接受一個mWork參數,這個mWork是一個和mFuture一樣頂一個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); } };
mWorker 其實是一個Callable的實例了,他有個call方法。而mFuture本質上是一個Runnable的實例,調用mFuture的run方法其實就是調用FutureTask中的run方法,FutureTask接受了mWorker 作為參數,並把它賦值給自己的callable 屬性,代碼如下:
public FutureTask(Callablecallable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
所以我們看看FutureTask的run方法
public void run() { if (state != NEW || !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) return; try { Callablec = 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); } }
這個方法做了如下事情:
1.調用callable的call方法,也就是mWork的call方法,mWork的call方法step 3中已經貼過,其中會調用doInBackground方法,也就是我們提交的需要在後台執行的代碼了。因此,doInBackground是在mWork的call方法中被執行的,mWork的call方法又是通過THREAD_POOL_EXECUTOR.execute(mActive)被添加到線程池後被執行的。
2.調用postResult方法
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; }
postResult方法會發送一個消息,這個消息會使得InternalHandler的handleMessage方法被調用。這個時候,我們已經離開了後台線程,又回到UI線程了。因為InternalHandler的Looper是UI線程的Looper,其構造函數中有如下代碼可以知曉:
public InternalHandler() { super(Looper.getMainLooper()); }
注意我們獲取的消息為MESSAGE_POST_RESULT,因此根據handleMessage的定義:
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; } }
會調用到AsyncTask的finish方法:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
這個方法中,我們沒有調用取消方法的話,就會調用onPostExecute方法了,這樣,我們使用AsyncTask需要覆寫的幾個方法只有onProgressUpdate方法沒有調用了。那麼這個方法是怎麼被調用的呢?我們說,我們要更新進度的話,需要使用publishProgress方法,我們看看這個方法:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult
這個方法就是發送一個消息,然後還是InternalHandler方法handleMessage方法被調用,注意這會消息是MESSAGE_POST_PROGRESS,因此根據源碼,再貼一次handleMessage方法吧:
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; } }
可見這次調用的是onProgressUpdate方法。至此,所有需要覆寫的方法的調用過程我們都分析結束了。
該篇為ListView下拉刷新和上拉加載實現的各種方法大合集。可能在具體的細節邏輯上處理不太到位,但基本上完成邏輯的實現。細節方面,個人可以根據自己的需求進行完善。該博客
這篇文章主要介紹發送驗證碼和校驗驗證碼的功能,用到一個第三方平台Bmob,那Bmob是什麼呢?Bmob可以開發一個雲存儲的移動應用軟件,他提供了大量的標准的A
本文給大家介紹Activity的生命周期,如果大家學習過iOS的小伙伴的話,Activity的生命周期和iOS中ViewController的生命周期非常類似。生命周期,
1,搭建本地NDK環境 Build path中設置C/C++ build Build command ndk-build NDK_DEBUG=1C/C++ General