public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(10); 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 ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final int MESSAGE_POST_CANCEL = 0x3; private static final InternalHandler sHandler = new InternalHandler(); private final WorkerRunnable<Params, Result> mWorker; private final FutureTask<Result> mFuture; private volatile Status mStatus = Status.PENDING; /** * Indicates the current status of the task. Each status will be set only once * during the lifetime of a task. */ 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, } /** * 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 { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { Message message; Result result = null; try { result = 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) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " + "doInBackground()", t); } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } }; } /** * Returns the current status of this task. * * @return The current status. */ public final Status getStatus() { return mStatus; } protected abstract Result doInBackground(Params... params); protected void onPreExecute() { } @SuppressWarnings({"UnusedDeclaration"}) protected void onPostExecute(Result result) { } @SuppressWarnings({"UnusedDeclaration"}) protected void onProgressUpdate(Progress... values) { } protected void onCancelled() { } public final boolean isCancelled() { return mFuture.isCancelled(); } public final boolean cancel(boolean mayInterruptIfRunning) { 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); } public final AsyncTask<Params, Progress, Result> execute(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; sExecutor.execute(mFuture); return this; } protected final void publishProgress(Progress... values) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } private void finish(Result result) { onPostExecute(result); mStatus = Status.FINISHED; } 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; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); 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的全部代碼。
AsyncTask多用於Android的Context(Activity)處理后台邏輯同時又要兼顧主線程的一些邏輯(比如說Activity的UI更新)。
AsyncTask它其實是封裝了一個多線程、向開發者屏蔽了考慮多線程的問題。開發人員只需去重寫AsyncTask中的doInBackground、onProgressUpdate、onPreExecute、onPostExecute等方法,然后生成該對象並執行execute方法即可實現異步操作。
由於Android負責Context(Activity)的主線程(或者說是UI控制線程)不是線程安全的,也就是說開發者不能在自己生成的線程對象里面操作Activity的UI更新。
對於習慣了Java開發的開發人員來說,非要自己生成線程對象,在這個線程里操作一些時間較長的邏輯(比如下載文件),完成之后再提醒用戶下載完成(UI更新,比如在一個TextView中把文字修改成"下載完成")的話,那么UI更新的工作只能借助於Handler(這個Handler必須是在主線程中生成,或者說和主線程共用一個Looper和MessageQueue,通常的方法是在Activity中的onCreate方法中生成重寫的Handler對象)。當開發者的線程完成邏輯操作之后,發送一個消息到Handler,再由Handler去處理。如果Handler由主線程生成,那么這個Handler的handleMessage()會在主線程中執行,因此在handleMessage()中可以訪問Activity中的某個View並修改。如一下代碼:
public class TestActivity extends Activity{ private static final int WHAT = 0x01;<br> private Thread downloadThread; private Handler refleshHandler; @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.main); super.onCreate(savedInstanceState); downloadThread = new Thread(new DownloadRunnable()); refleshHandler = new RefleshHandler(); downloadThread.start(); } private class DownloadRunnable implements Runnable { public void run() { System.out.println("開始處理業務"); //耗時較長的邏輯代碼省略 refleshHandler.sendEmptyMessage(WHAT); } } private class RefleshHanler extends Handler{ @Override public void handleMessage(Message msg) { if(msg.what == WHAT){ //更新TestActivity UI } } }
上述的方法也未嘗不是一種好的方法。然而Android為開發者提供了一種新的解決方案,那就是AsyncTask,分析完源代碼會發現,其實AsyncTask為我們封裝了上述的方法。
對於我們重寫AsyncTask的doInBackground()的方法,AsyncTask會將它封裝成一個實現Callable接口的對象,在線程池中找出一個線程是實現它,因此我們所需要的耗時較長的邏輯可以放在這個方法里面。
AsyncTask也維護着一個靜態的Handler,這個Handler屬於創建AsyncTask的線程,而創建AsyncTask一般都是主線程(UI線程),因此這個Handler可以訪問並Activity的UI。
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; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }
可以看到這個Handler操作了AsyncTask的三個方法:finish() , onProgressUpdate() , onCancelled(),而finish() 又調用onPostExecute()方法。
可以這么說,onProgressUpdate() ,onCancelled(),onPostExecute() 用來被開發者重寫,去更新UI的,這3個方法會涉及到UI的操作,因此doInBackground()方法里不能調用這幾個方法,也就是說開發者可以重寫這些方法,但是又不能直接調用這些方法,只能通過發送消息給Handler的方式來隱式的調用。
onProgressUpdate()是一個更新處理進度的方法,開發者可以重寫它,可以將它關聯到Activity的一個進度條控件上,在doInBackground()里可以用publishProgress()去間接調用它(其實這個函數也是通過發送what為MESSAGE_POST_PROGRESS的Message給Handler的方式來調用onProgressUpdate()的),這樣就能在UI上顯示進度信息。
finish()方法,也就是onPostExecute(),是處理完doInBackground()得到結果之后的調用。doInBackground()的函數封裝在實現Callable接口的名叫一個WorkerRunnable的抽象類中,再將這個類封裝在Future中,並重寫Future的done()方法,這個方法會在Callable的call方法執行完之后調用,就是doInBackground()方法執行完之后調用,它可以獲得執行完的結果,Future.get()方法(可能阻塞),並將得到的結果封裝在what為MESSAGE_POST_RESULT的Message中,並發送。上訴的核心代碼如下:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { Message message; Result result = null; try { result = 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) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " + "doInBackground()", t); } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } };
同理,onCancelled()是在AsyncTask被取消時的調用,也是通過Handler的方法。
有一點要注意到,還有一個onPreExecute()方法,雖然這個方法是未被Handler加入到消息處理的方法里,但是這個方法是在execute()里執行的,execute是主線程(UI線程)才會去執行的,所以這個方法也能訪問和修改UI。
總結: 開發者可以重寫AsyncTask的onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute(), doInBackground()方法,以得到異步實現后台邏輯並更新UI的操作。
其中onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute() 可以直接訪問並修改UI。
但是doInBackground()不能出現涉及UI的操作,也不能直接調用onProgressUpdate() , onCancelled(), onPostExecute(), onPreExecute() 這四個方法,后三者不需要調用,可以通過publishProgress()去間接的調用onProgressUpdate()方法。
最后要說的是AsyncTask的對象一旦生成之后,execute()方法只能被調用一次,即使是同樣的操作,也需要重新生成AsyncTask對象才行。
PS:本文是原創,作者正在找工作中,適合Android和J2EE的開發。有意者請發送郵件到 answer1991.chen@gmail.com 。