Android中的AsyncTask解析
概述
AsyncTask是一個抽象的泛型類,它是一個輕量級的異步任務類,它可以在線程池中執行後臺任務,然後把執行的進度和後臺結果傳遞給主線程並在主線程中更新UI。AsyncTask封裝了Thread和Handler。但是AsyncTask並不適合耗時的後臺任務,對於耗時的後臺任務建議使用線程池。它提供了Params、Progress和Result三個泛型參數。提供了四個核心方法(JAVA中...表示參數的數量不定,它是一種數組性參數):
①、onPreExecute():運行在主線程,在異步任務執行之前調用,通常用來進行一些初始化操作。
②、doInBackground(Params...):在線程池中執行,執行比較耗時的操作。可以通過publishProgress()方法來更新任務的進度。
③、onProgressUpdate(Progress...):在主線程中執行,當後臺任務的執行進度發生改變時此方法會被調用。
④、onPostExecute(Result):在主線程中執行,在異步任務執行之後此方法會被調用。參數爲doInBackground(Params...)的返回值。
繼承自AsyncTask的類至少需要重寫doInBackground()方法。
注意事項
①、AsyncTask必須在主線程中創建,execute()方法也必須在主線程中調用。
②、不要手動調用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()這幾個方法。
③、一個AsyncTask對象只能執行一次。
④、Android 1.6之前,AsyncTask是串行執行任務的,Android 1.6時AsyncTask開始使用線程池處理並行任務。但從Android 3.0開始爲了避免AsyncTask所帶來的併發錯誤,AsyncTask又採用一個線程來串行執行任務。
執行流程分析
以一個異步加載圖片的例子,按着執行過程一步步分析。
我們首先寫了繼承了AsyncTask的MyTask類,三個參數分別爲URL、Float、Bitmap。重寫了構造方法,要求傳入ImageView,以便在onPostExecute()方法中把圖片設置上去。重寫了doInBackground(URL...params)方法,使用了HttpURLConnection來獲取圖片,並通過調用publicProgress()方法來更新進度。onProgressUpdate(Float...values)中使用Log輸出的方式展示圖片加載進度、在onPostExecute(Bitmap)中將圖片設置到ImageView上。
public class MyTask extends AsyncTask<URL, Float, Bitmap> {
private ImageView iv = null;
public MyTask(ImageView iv) {
this.iv = iv;
}
@Override
protected Bitmap doInBackground(URL... params) {
Log.e("MyTask", "開始");
URL url = params[0];
Bitmap bitmap = null;
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
InputStream is = conn.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
int count = 0;
int length = conn.getContentLength();
while ((len = is.read(buffer)) != -1) {
bos.write(buffer, 0, len);
count += len;
publishProgress(count * 1.0f / length);
}
bos.close();
is.close();
byte[] byteArray = bos.toByteArray();
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bit) {
super.onPostExecute(bit);
iv.setImageBitmap(bit);
}
@Override
protected void onProgressUpdate(Float... values) {
super.onProgressUpdate(values);
Log.e("MyTask", "進度:" +values[0]);
}
}
在主線程中創建MyTask實例:
MyTask t = new MyTask(mIv);
t.execute(url);
JAVA規定如果子類構造方法中沒有顯式調用父類的構造方法(也就是沒有super()語句),會默認調用父類的無參構造方法。故調用AsyncTask的構造方法,如下:
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 static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
上面的構造方法中,WorkerRunnable是一個維護了Params[]數組的Callable的子類抽象類,重寫了它的call方法後在構造方法中創建了一個WorkerRunnable對象mWorker。FutureTask是一個實現了FutureRunnable的類,提供了start、cancel方法,也提供了查詢任務當前狀態的方法,可以通過FutureTask類封裝Runnable或者Callable對象。在AsyncTask構造方法中,創建了一個FutureTask的實例mFuture封裝了mWorker,並重寫了done()方法。
接下調用MyTask對象的execute(URL)方法:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
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();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
由上面的源碼可以看出,execute()方法中調用了executeOnExecutor()方法,並傳入sDefaultExecutor。sDefaultExecutor是一個串行的線程池,一個進程中的所有串行任務都在這個線程池中排隊執行。在executeOnExecutor()方法中,先判斷了當前任務的狀態。如果非PENDING,而是RUNNING或者FINISHED,說明這個任務要麼正在執行,要麼已經結束,拋出異常。否則將這個任務的狀態設置爲RUNNING,執行onPreExecute()方法。將傳進的params參數設置給mWorker,然後調用sDefaultExecutor的execute()方法
。
接下來進入串行線程池sDefaultExecutor的execute方法看看:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
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);
}
}
}
這個串行線程池SerialExecutor實現了Executor接口。重寫了Executor類中的唯一方法execute(Runnable)。在execute()方法中,先將傳進來的Runnable對象mFuture再封裝成一個Runnable,然後添加進mTask隊列。然後判斷當前活動的任務是否爲null,是的話就調用scheduleNext()方法安排下一個任務。在scheduleNext()方法中,如果任務隊列不爲空則出隊一個任務,使用線程池THREAD_POOL_EXECUTOR來執行任務。
JVM會調用mActive的run()方法。在它的run()方法中,先調用了mFutrue的run()方法(mFuture被封裝爲一個Runnable加入mTasks隊列),再在finally塊中調用scheduleNext(),進行下一個任務。
在調用mFuture的run()方法時,會調用到mWorker的call()方法。如下:
public void run() {
......
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
......
}
} finally {
......
}
}
故此時回到mWorker的call()方法:
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));
}
};
call()方法中調用了doInBackground(mParams)方法,執行我們重寫的doInBackground方法。然後把該方法的返回結果作爲參數傳入postResult(Result)方法中。來到postResult()方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在postResult(Result)方法中,通過handler獲取到消息池中的一個消息。注意這裏的getHandler()方法獲取一個靜態的Handler對象sHandler,sHandler是用於將執行環境切換到主線程中的Handler,這也就是爲什麼AsyncTask必須在主線程中加載的原因。它是實現了Handler的靜態內部類InternalHandler的實例。如下:
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;
}
}
}
InternalHandler重寫了handleMessage(Message)方法。回到postResult(Result)方法中。獲取到sHandler之後,把傳入的Result封裝成AsyncTaskResult對象,作爲消息的obj傳進obtainMessage(int, Object)方法中。內部靜態類AsyncTaskResult類中維護了一個AsyncTask成員變量和Data數組(即Result)。接下來通過sHandler進行消息的分發。在handleMessage中,匹配到MESSAGE_POST_RESULT,於是調用AsyncTaskResult中的AsyncTask對象的finish()方法,把結果傳進去:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在finish方法中,根據情況調用onCancelled()方法或onPostExecute()方法,最後把任務的狀態置爲Status_FINISHED。至此一個任務就執行完畢。
最後再看看任務進度的更新。回到MyTask類中,在doInBackground()方法中,我調用了publishProgress(float)方法,這個方法用於發送一個MESSAGE_POST_PROGRESS消息給主線程。匹配到sHandler的handleMessage()中的MESSAGE_POST_PROGRESS,執行自己重寫的onProgressUpdate()方法。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
在Android 3.0以上的阪本中任務默認是串行執行的,要使任務並行執行,只要採用AsyncTask的executeOnExecutor方法。如下:
new MyTask(...).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.