主線程和子線程
主線程是指進(jìn)程所擁有的線程,在Java中默認(rèn)一個(gè)進(jìn)程只有一個(gè)線程,這個(gè)線程就是主線程。主線程主要用于處理界面交互相關(guān)的邏輯,因?yàn)橛脩綦S時(shí)都可能會(huì)和界面發(fā)生交互,因此主線程在任何時(shí)候都必須要有較快的響應(yīng)速度,否則就會(huì)給人一種卡頓的感覺(jué)。這就要求主線程不能處理太耗時(shí)的任務(wù),比如網(wǎng)絡(luò)請(qǐng)求、IO操作等。這樣,只能把網(wǎng)絡(luò)請(qǐng)求、IO這些耗時(shí)操作放到子線程中去處理。所以,正常一個(gè)帶有網(wǎng)絡(luò)接口請(qǐng)求的應(yīng)用,那么至少會(huì)需要兩個(gè)線程,一個(gè)主線程負(fù)責(zé)處理交互,一個(gè)子線程負(fù)責(zé)處理接口請(qǐng)求,那么子線程請(qǐng)求接口之后拿到的數(shù)據(jù),怎么傳遞給主線程去更新UI呢?我們可以用Handler來(lái)處理子線程與主線程的交互。而AsyncTask就是封裝了Handler+線程池來(lái)處理子線程與主線程交互的一個(gè)API,方便了開(kāi)發(fā)者,不用每次都自己創(chuàng)建線程和寫Handler了。
AsyncTask基本使用
創(chuàng)建一個(gè)下載文件的異步任務(wù)
private class DownloadFileTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
執(zhí)行該異步任務(wù)
DownloadFileTask downloadTask = new DownloadFileTask().execute(url1,url2,url3);
AsyncTask是一個(gè)抽象的泛型類,主要接受三個(gè)參數(shù),分別是Params、Progress、Result,其中Params主要是AsyncTask執(zhí)行時(shí)傳遞進(jìn)來(lái)的參數(shù)類型,Progress是在異步任務(wù)執(zhí)行的過(guò)程中的參數(shù)類型,Result是異步任務(wù)結(jié)束之后的類型。另外,AsyncTask提供了4個(gè)核心方法,分別如下:
- onPreExecute(),在主線程中執(zhí)行,在異步任務(wù)開(kāi)始執(zhí)行之前調(diào)用,一般用于任務(wù)執(zhí)行前的準(zhǔn)備工作
- doInBackground(Params... param),在線程池中執(zhí)行,開(kāi)始執(zhí)行異步執(zhí)行任務(wù),param是通過(guò)AsyncTask的execute方法傳遞進(jìn)來(lái)的,在這個(gè)方法中可以通過(guò)publishProgress來(lái)更新任務(wù)的進(jìn)度,這個(gè)方法會(huì)調(diào)用到onProgressUpdate
- onProgressUpdate(Progress... progress),在主線程中執(zhí)行,用于更新進(jìn)度。
- onPostExecute(Result result),在主線程中執(zhí)行,異步任務(wù)執(zhí)行結(jié)束之后執(zhí)行,其中result參數(shù)是doInBackground的返回值。
取消AsyncTask異步任務(wù)
downloadTask.cancel(true);
注意:這里的cancel方法的參數(shù)既可以傳true也可以傳false,其中true表示可以中斷正在執(zhí)行的任務(wù)。另外,雖然調(diào)用了這個(gè)cancel方法,但是doInbackground的異步任務(wù),并不會(huì)立即結(jié)束,只是不會(huì)調(diào)用onProgressUpdate和onPostExecute。舉個(gè)例子,doInBackground中有個(gè)循環(huán),當(dāng)執(zhí)行cancel并不會(huì)中斷這個(gè)循環(huán),如果需要中斷這個(gè)循環(huán),那么可以在doInBackground中添加一個(gè)判斷邏輯,如果isCancelled()則return。
AsyncTask在具體的使用過(guò)程中也有一些條件限制,主要有如下幾點(diǎn):
- AsyncTask這個(gè)類必須在主線程中加載,不過(guò)這個(gè)在Android4.1及以上版本中已經(jīng)被系統(tǒng)自動(dòng)完成。
- 必須在主線程創(chuàng)建該實(shí)例對(duì)象
- 必須在主線程調(diào)用execute(Params...)
- 不要手動(dòng)調(diào)用上面的四個(gè)核心方法
- 一個(gè)AsyncTask任務(wù)只能execute一次,否則會(huì)報(bào)運(yùn)行時(shí)異常
AsyncTask的發(fā)展史
在一開(kāi)始出現(xiàn)AsyncTask時(shí),只有一個(gè)子線程來(lái)執(zhí)行異步任務(wù)。從Android1.6開(kāi)始,從單線程改成了線程池了支持并發(fā)任務(wù)了。然后從3.0開(kāi)始為了解決并發(fā)導(dǎo)致的錯(cuò)誤又改成了串行執(zhí)行了。如果你還是想并發(fā)執(zhí)行任務(wù),可以調(diào)用AsyncTask的executeOnExecutor方法。
AsyncTask的工作原理
為了分析AsyncTask的工作原理,我們先分析它的execute方法,execute方法又調(diào)用了executeOnExecutor方法,它們的實(shí)現(xiàn)如下:
@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;
}
可以看到,在這里已經(jīng)開(kāi)始調(diào)用了onPreExecute方法,然后把Params封裝到FutureTask對(duì)象中了,最后開(kāi)始執(zhí)行sDefaultExecutor這個(gè)默認(rèn)線程池。下面分析這個(gè)線程池的執(zhí)行過(guò)程,代碼如下:
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的實(shí)現(xiàn)可以分析出多個(gè)AsyncTask同時(shí)執(zhí)行默認(rèn)是串行的。SerialExecutor首先會(huì)將FutureTask插入到隊(duì)列中,如果發(fā)現(xiàn)沒(méi)有正在執(zhí)行的Task,那么就從隊(duì)列中取出一個(gè)Task放到THREAD_POOL_EXECUTOR線程池中執(zhí)行,當(dāng)執(zhí)行完成之后再會(huì)調(diào)用scheduleNext,直到mTasks中的Task都執(zhí)行完成為止。FutureTask已經(jīng)放到THREAD_POOL_EXECUTOR線程池中了,那么就會(huì)開(kāi)始執(zhí)行這個(gè)FutureTask了,F(xiàn)utureTask的實(shí)現(xiàn)如下:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
FutureTask的執(zhí)行從call方法開(kāi)始,在call中調(diào)用了第二個(gè)核心方法,就是doInBackground,然后拿到doInBackground的返回值通過(guò)postResult(result)傳遞給AsyncTask的另外一個(gè)重要角色I(xiàn)nternalHandler,這樣就通過(guò)Handler機(jī)制實(shí)現(xiàn)了從子線程傳遞數(shù)據(jù)到主線程了,然后在Handler中執(zhí)行了第四個(gè)重要方法onPostExecute來(lái)更新UI。InternalHandler實(shí)現(xiàn)如下:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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;
}
}
}
可以發(fā)現(xiàn)InternalHandler是個(gè)靜態(tài)的內(nèi)部類,為了能夠?qū)?zhí)行環(huán)境切換到主線程,這就要求創(chuàng)建的Handler對(duì)象必須在主線程創(chuàng)建。由于靜態(tài)成員必須在類加載時(shí)初始化,所以才會(huì)要求AsyncTask的類必須在主線程中加載。
參考:
https://developer.android.com/reference/android/os/AsyncTask
《Android開(kāi)發(fā)藝術(shù)探索-任玉剛》