AsyncTask使用
AsyncTask是一個(gè)泛型類(lèi),它提供了Params,Progress,Result三個(gè)泛型參數(shù)。Params用來(lái)指定異步任務(wù)的參數(shù)類(lèi)型,Progress指定后臺(tái)任務(wù)執(zhí)行進(jìn)度的參數(shù)類(lèi)型,Result指定后臺(tái)任務(wù)返回的參數(shù)類(lèi)型,它有以下4個(gè)主要方法:
-
onPreExecute()
主線(xiàn)程中執(zhí)行,開(kāi)始異步任務(wù)之前調(diào)用,用于準(zhǔn)備工作 -
doInBackground(Params...params)
執(zhí)行在線(xiàn)程池中,執(zhí)行異步任務(wù)的主要方法。它內(nèi)部可以調(diào)用publishProgress(Progress...values)
來(lái)通知onProgressUpdate
更新進(jìn)度,它還需要返回Result給onPostExecute(Result result)
方法 -
onProgressUpdate(Progress...values)
主線(xiàn)程中執(zhí)行,主要用于更新進(jìn)度 -
onPostExecute(Result result)
主線(xiàn)程中執(zhí)行,異步任務(wù)執(zhí)行結(jié)束后調(diào)用
除此之外,它還提供了一個(gè)onCancelled()
方法,當(dāng)異步任務(wù)被取消時(shí)此方法被調(diào)用,并且onPostExecute(Result result)
不再執(zhí)行。
典型的AsyncTask用法如下:
//自定義Task
class MyTask extends AsyncTask<String,Float, Bitmap> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Bitmap doInBackground(String... params) {
InputStream is = null;
ByteArrayOutputStream baos = null;
byte[] data = null;
try {
HttpURLConnection connection = (HttpURLConnection)new URL(params[0]).openConnection();
is = connection.getInputStream();
int totalLength =0;
totalLength = connection.getContentLength();
baos = new ByteArrayOutputStream();
byte[] buf = new byte[128];
int length ;
int count = 0;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
publishProgress(count * 1.0f / totalLength);
}
data = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Bitmap bitmap= BitmapFactory.decodeByteArray(data, 0, data.length);
//Bitmap bitmap = BitmapFactory.decodeStream(is);//返回null
Log.d("mytask", "BitMap is " + bitmap);
return bitmap;
}
@Override
protected void onProgressUpdate(Float... values) {
Log.d("mytask", "Progress is " + values[0]*100 + "%");
}
@Override
protected void onPostExecute(Bitmap bitmap) {
MyImage.setImageBitmap(bitmap);
}
}
//主線(xiàn)程中調(diào)用task:
new MyTask().execute("http://192.168.1.105:8080/largeicon.png");
使用AsyncTask有幾點(diǎn)注意事項(xiàng):
- Asynctask對(duì)象必須在主線(xiàn)程創(chuàng)建
- execute方法必須在主線(xiàn)程調(diào)用
- 不要在外部手動(dòng)調(diào)用AsyncTask的四個(gè)主要內(nèi)部方法
- 一個(gè)AsyncTask對(duì)象只能執(zhí)行一次,否則會(huì)報(bào)錯(cuò)
- AsyncTask的執(zhí)行除了execute之外,還有
executeOnExecutor
方法
AsyncTask源碼解析:
我們從execute方法作為入口,開(kāi)始一步一步看一下AsyncTask是怎樣工作的,execute源碼如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看到,調(diào)用AsyncTask的execute方法之后,除了我們傳入的參數(shù)之外,它另外加了一個(gè)sDefaultExecutor
,這是一個(gè)調(diào)度任務(wù)用的串行線(xiàn)程池,稍后會(huì)詳細(xì)介紹,實(shí)際上執(zhí)行的是executeOnExecutor
方法,源碼如下:
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;
}
首先會(huì)進(jìn)入狀態(tài)判斷,如果任務(wù)已經(jīng)在執(zhí)行,或者執(zhí)行過(guò)了,會(huì)拋出異常,這也就是為什么一個(gè)AsyncTask只能執(zhí)行一次的原因了。
繼續(xù)往下,onPreExecute()
在這里執(zhí)行了,此時(shí)還沒(méi)有出現(xiàn)與線(xiàn)程相關(guān)的操作,所以onPreExecute()
一定是運(yùn)行在主線(xiàn)程的。
下面的兩句就是執(zhí)行異步任務(wù)的核心了,首先將傳入的參數(shù)賦值給了mWorker.mParams
,然后調(diào)用exec.execute(mFuture)
,這里的exec默認(rèn)情況下是sDefaultExecutor
,這里調(diào)用了它的execute方法。
上面已經(jīng)提到了,sDefaultExecutor
是一個(gè)執(zhí)行調(diào)度任務(wù)的線(xiàn)程池,它指向的是AsyncTask類(lèi)中的靜態(tài)變量,SERIAL_EXECUTOR
,也就是說(shuō),所有的AsyncTask對(duì)象,都是通過(guò)一個(gè)線(xiàn)程池來(lái)調(diào)度的,它就是SERIAL_EXECUTOR
。
從名字就可以看出,SERIAL_EXECUTOR
是串行的,源碼如下:
//定義SERIAL_EXECUTOR
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//SerialExecutor類(lèi):
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
的內(nèi)容,首先定義了一個(gè)隊(duì)列mTasks
,用來(lái)存放它接收到的任務(wù)。在execute方法中,首先在隊(duì)列mTasks
的尾部添加一個(gè)任務(wù),這個(gè)任務(wù)的內(nèi)容包括兩個(gè)部分:
- 執(zhí)行參數(shù)
Runnable對(duì)象
的run
方法(這里的參數(shù)是mFuture
,稍后會(huì)查看run方法的內(nèi)容) - 執(zhí)行
scheduleNext
。
注意,mTasks.offer(.....);
這一步只是將任務(wù)添加到隊(duì)列的尾部,實(shí)際上并未執(zhí)行。
直到if (mActive == null)
,執(zhí)行任務(wù)之前會(huì)進(jìn)行判斷。如果這是AsyncTask對(duì)象的第一次執(zhí)行,那么條件成立,調(diào)用scheduleNext
,在這里先將mActive
指向下一個(gè)任務(wù),然后又調(diào)用了THREAD_POOL_EXECUTOR
的execute
方法,在這里異步任務(wù)將得到最終的執(zhí)行。
注意synchronized
的存在,使得線(xiàn)程池只能有一個(gè)線(xiàn)程來(lái)執(zhí)行Runnable對(duì)象,從而保證了任務(wù)會(huì)按添加順序串行執(zhí)行。
異步任務(wù)開(kāi)始執(zhí)行以后,由于mTasks
的任務(wù)中包含了scheduleNext
,所以,只要隊(duì)列中還有任務(wù),執(zhí)行完一個(gè)之后會(huì)繼續(xù)執(zhí)行下一個(gè),直到任務(wù)全部執(zhí)行完畢。
這里我們先不分析THREAD_POOL_EXECUTOR
,只需要知道,它是AsyncTask的最終執(zhí)行者。再回到mTasks
,可以看到執(zhí)行任務(wù)時(shí)調(diào)用的是mFuture
的run方法。
相關(guān)的關(guān)鍵代碼如下:
//AsyncTask構(gòu)造函數(shù)中對(duì)mWorker ,和mFuture賦值
//mWorker是一個(gè)Runnable對(duì)象,封裝了doInBackground和postResult方法,等待執(zhí)行。只有它內(nèi)部封裝的方法會(huì)在子線(xiàn)程執(zhí)行
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception { ---------->###1
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//后臺(tái)任務(wù)執(zhí)行方法
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);//該方法通過(guò)Handler將消息發(fā)送到主線(xiàn)程
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
......
};
//mFuture 對(duì)應(yīng)FutureTask類(lèi),主要方法如下
//mFuture 賦值時(shí)調(diào)用此構(gòu)造函數(shù),將mWorker傳遞給了成員變量callable ;
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//mFuture 的run方法
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
//取出callable,實(shí)際上就是傳入的mWorker
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//執(zhí)行mWorker的call方法,并返回結(jié)果,call方法在AsyncTask構(gòu)造函數(shù)中對(duì)mWorker賦值時(shí)覆寫(xiě),上文###1處
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
......
}
到這里,整個(gè)邏輯就清楚了。 AsyncTask將doInBackground
方法封裝到mWorker中 ,mFuture 又接管了mWorker 的執(zhí)行,最后將mFuture 交給THREAD_POOL_EXECUTOR
的execute方法,最后doInBackground
會(huì)在THREAD_POOL_EXECUTOR
中運(yùn)行。
THREAD_POOL_EXECUTOR
通過(guò)名字我們也能大致猜到,這是一個(gè)線(xiàn)程池。它的創(chuàng)建代碼如下:
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;
實(shí)際上,AsyncTask中構(gòu)建了一個(gè)核心線(xiàn)程數(shù)等于CPU核心數(shù)+1,最大線(xiàn)程數(shù)為CPU核心數(shù)2倍的線(xiàn)程池,用來(lái)執(zhí)行AsyncTask任務(wù)。
通過(guò)上邊的分析,我們可以看到,雖然AsyncTask任務(wù)最終是在線(xiàn)程池中執(zhí)行的,但是由于SerialExecutor
的存在,線(xiàn)程會(huì)被丟進(jìn)隊(duì)列中,從第一個(gè)開(kāi)始排隊(duì)執(zhí)行,所以仍然是串行的。
實(shí)際上Android在這里有過(guò)幾次變動(dòng),在Android1.6之前是串行的,1.6-3.0采用線(xiàn)程池并行處理,3.0以后又改為串行處理了,但是我們可以通過(guò)executeOnExecutor來(lái)指定AsyncTask使用線(xiàn)程池來(lái)并行處理:
new MyTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"http://192.168.1.105:8080/largeicon.png");