1 AsyncTask基礎(chǔ)
1.1 AsyncTask作用
AsyncTask是安卓開發(fā)中使用的一種輕量級異步任務(wù)類朱浴。其作用是在線程池中執(zhí)行后臺任務(wù)罐韩,并在執(zhí)行過程中將執(zhí)行進度傳遞給主線程徊哑,當任務(wù)執(zhí)行完畢后,將最終結(jié)果傳遞給主線程壶熏。
1.2 AsyncTask產(chǎn)生背景
安卓系統(tǒng)線程分為主線程和子線程陶夜,主線程也叫UI線程。主線程主要負責與用戶交互常侣。為了更好的用戶體驗茵肃,保證系統(tǒng)不因主線程的阻塞而產(chǎn)生卡頓,安卓系統(tǒng)要求主線程中不能執(zhí)行耗時任務(wù)袭祟。例如:IO操作验残、網(wǎng)絡(luò)請求等必須在子線程中完成。AsyncTask就是為了適應(yīng)這種需要而產(chǎn)生巾乳。
1.3 AsyncTask使用場景
AsyncTask是一種輕量級的異步線程您没,雖然使用AsyncTask可以更加方便的執(zhí)行后臺任務(wù)與在主線程中訪問UI,但是AsyncTask不適合執(zhí)行特別耗時的后臺任務(wù)胆绊。具體原因會在后續(xù)分析中闡明氨鹏。
1.4 AsyncTask使用方式
AsyncTask原型:
public abstract class AsyncTask<Params, Progress, Result>
由原型可見AsyncTask是一個泛型抽象類。
參數(shù)說明:
Params | 執(zhí)行后臺任務(wù)所需參數(shù)類型 |
---|---|
Progress | 后臺任務(wù)執(zhí)行進度的類型 |
Result | 后臺任務(wù)執(zhí)行完畢后返回結(jié)果類型 |
AsyncTask 核心方法:
AsyncTask提供4個核心方法:
1) protected void onPreExecute()
執(zhí)行線程 | 主線程 |
---|---|
調(diào)用時間 | 異步任務(wù)執(zhí)行之前 |
方法作用 | 異步任務(wù)執(zhí)行前的初始化工作 |
2)protected Result doInBackground(Params...params)
執(zhí)行線程 | 線程池中執(zhí)行 |
---|---|
調(diào)用時間 | 任務(wù)開始后到任務(wù)結(jié)束之前 |
方法作用 | 用于執(zhí)行異步任務(wù) |
3)protected void onProgressUpdate(Prgress...values)
執(zhí)行線程 | 主線程 |
---|---|
調(diào)用時間 | 任務(wù)開始后到任務(wù)結(jié)束之前 |
方法作用 | 用于更新任務(wù)進度 |
4)protected void onPostExecute(Result result)
執(zhí)行線程 | 主線程 |
---|---|
調(diào)用時間 | 異步任務(wù)執(zhí)行之后 |
方法作用 | 將異步任務(wù)的執(zhí)行結(jié)果傳遞給主線程 |
了解各個參數(shù)以及方法含義后我們來看一段官方給出的AsyncTask示例程序:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
//任務(wù)執(zhí)行前的準備工作
protected void onPreExecute(){
}
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會調(diào)用onProgressUpdate
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
//主線程中調(diào)用仆抵,主要用于更新后臺任務(wù)進度
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
//任務(wù)執(zhí)行完畢
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
DownloadFilesTask類模擬文件下載過程。傳入的參數(shù)Params類型為URL(文件地址)种冬,后臺任務(wù)進程參數(shù)Progress類型為Integer(下載進度)镣丑,后臺任務(wù)返回結(jié)果參數(shù)Result類型為Long(總文件大小)。
1.5 AsyncTask使用注意
通過簡單的官方示例程序?qū)W習了AsyncTask的使用方法娱两,那么使用AsyncTask需要注意以下幾個條件:
1)AsyncTask類必須在主線程加載
2)AsyncTask對象必須在主線程創(chuàng)建
3)execute方法必須在主線程調(diào)用
4)不要在程序中直接調(diào)用AsyncTask提供的4個核心方法
5)一個AsyncTask對象只能執(zhí)行一次莺匠,即只能調(diào)用一次execute
1.6 小結(jié)
通過本節(jié)的介紹,對AsyncTask有了初步的認識十兢,第二部分將從AsyncTask源碼出發(fā)趣竣,分析AsyncTask的工作原理以及回答本節(jié)中留下的問題摇庙。
2 源碼分析
在第1小節(jié)中主要介紹了AsyncTask的基本知識,本節(jié)將從源碼出發(fā)遥缕,深層次分析AsyncTask的工作原理卫袒。
2.1 AsyncTask對象創(chuàng)建
使用AsyncTask時,一般根據(jù)具體的任務(wù)繼承AsyncTask類单匣,例如:
private class MyAsyncTask extends AsyncTask<String, Integer, String> {
protected String doInBackground(String... args1) {
Log.i(TAG, "doInBackground in:" + args1[0]);
int times = 10;
for (int i = 0; i < times; i++) {
publishProgress(i);//提交之后玛臂,會執(zhí)行onProcessUpdate方法
}
Log.i(TAG, "doInBackground out");
return "over";
}
/**
* 在調(diào)用cancel方法后會執(zhí)行到這里
*/
protected void onCancelled() {
Log.i(TAG, "onCancelled");
}
/**
* 在doInbackground之后執(zhí)行
*/
protected void onPostExecute(String args3) {
Log.i(TAG, "onPostExecute:" + args3);
}
/**
* 在doInBackground之前執(zhí)行
*/
@Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute");
}
/**
* @param args2
*/
@Override
protected void onProgressUpdate(Integer... args2) {
Log.i(TAG, "onProgressUpdate:" + args2[0]);
}
}
MyAsyncTask對象的創(chuàng)建必須在主線程中,使用方式為:
new MyAsyncTask().execute("AsyncTask Test");
2.2 execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute
方法調(diào)用了executeOnExecutor
方法并傳遞參數(shù)sDefaultExecutor和params封孙。
再看executeOnExecutor
方法:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//判斷當前狀態(tài)
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)");
}
}
//將狀態(tài)置為運行態(tài)
mStatus = Status.RUNNING;
//主線程中最先調(diào)用onPreExecute方法,進行準備工作
onPreExecute();
//將參數(shù)傳給mWorker
mWorker.mParams = params;
//調(diào)用線程池讽营,執(zhí)行任務(wù)
exec.execute(mFuture);
return this;
}
executeOnExecutor
方法首先判斷狀態(tài)虎忌,若處于可執(zhí)行態(tài),則將狀態(tài)置為RUNNING橱鹏。然后調(diào)用了onPreExecute
方法膜蠢,交給用戶進行執(zhí)行任務(wù)前的準備工作。核心部分在于 exec.execute(mFuture)
莉兰。exec即sDefaultExecutor挑围。
2.3 sDefaultExecutor串行線程池
查看sDefaultExecutor定義:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
sDefaultExecutor是一個串行線程池,作用在于任務(wù)的排隊執(zhí)行糖荒。其源碼如下:
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的源碼可以看出杉辙,mFuture是插入到mTasks任務(wù)隊列的對象。當mTasks中沒有正在活動的AsyncTask任務(wù)捶朵,則調(diào)用scheduleNext
方法執(zhí)行下一個任務(wù)蜘矢。若一個AsyncTask任務(wù)執(zhí)行完畢,則繼續(xù)執(zhí)行下一個AsyncTask任務(wù)综看,直至所有任務(wù)執(zhí)行完畢品腹。通過分析可以發(fā)現(xiàn)真正去執(zhí)行后臺任務(wù)的是線程池THREAD_POOL_EXECUTOR。
2.4 線程池THREAD_POOL_EXECUTOR
THREAD_POOL_EXECUTOR定義如下:
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;
}
2.5 mFuture并發(fā)執(zhí)行對象
線程池THREAD_POOL_EXECUTOR中執(zhí)行的Runnable對象為mFuture红碑。
mFuture的定義:
private final FutureTask<Result> mFuture;
mFuture為線程池執(zhí)行的真正任務(wù)舞吭,mFuture的執(zhí)行過程過程是怎樣的呢?
再看AsyncTask的構(gòu)造:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//創(chuàng)建mworker對象
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;
}
};
//創(chuàng)建mFuture對象
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);
}
}
};
}
通過AsyncTask的構(gòu)造中可以看到mWorker為FutureTask的構(gòu)造函數(shù)參數(shù)析珊,則FutureTask對象中持有mWorker的引用羡鸥。
FutureTask的構(gòu)造函數(shù):
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
FutureTask的run
方法:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = 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);
}
}
FutureTask的run
方法中調(diào)用了 result = c.call()
; 即調(diào)用了mWorker的call
方法。
2.6 mWorker對象
mWorker的call
方法:
mWorker = new WorkerRunnable<Params, Result>() {
//call方法
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//執(zhí)行任務(wù)
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
通過代碼可以發(fā)現(xiàn)忠寻,最終在線程池中執(zhí)行的是mWorker的call
方法兄春,call
方法中調(diào)用了doInBackground
方法,因此可以看出doInBackground
方法是在線程池中調(diào)用的锡溯。
當任務(wù)執(zhí)行完畢后則調(diào)用postResult
方法:
//任務(wù)執(zhí)行完后調(diào)用方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult
方法發(fā)送MESSAGE_POST_RESULT消息和result赶舆。
2.7 InternalHandler接收處理消息
InternalHandler代碼:
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;
}
}
}
InternalHandler接收到MESSAGE_POST_RESULT時調(diào)用result.mTask.finish(result.mData[0])
;
2.8 finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish
方法中若任務(wù)沒有取消則調(diào)用onPostExecute
方法發(fā)送結(jié)果哑姚,若任務(wù)取消則調(diào)用onCancelled
方法。finish方法是在主線程中執(zhí)行的芜茵。
2.9 onProgressUpdate
通過上述流程已經(jīng)順序找到了onPreExecute
叙量、doInBackground
、onPostExecute
方法九串,那么onProgressUpdate
是如何執(zhí)行的呢绞佩?
首先查看 publishProgress
方法:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
MyAsyncTask示例中在doInBackground
中調(diào)用publishProgress
方法,publishProgress
方法發(fā)送MESSAGE_POST_PROGRESS消息和進度values猪钮,InternalHandler在接收到MESSAGE_POST_PROGRESS消息中調(diào)用onProgressUpdate
方法品山。因此onProgressUpdate
也是在主線程中調(diào)用。
2.10 小結(jié)
通過上述一步步的源碼分析過程烤低,已經(jīng)掌握了AsyncTask任務(wù)的執(zhí)行過程肘交。AsyncTask中有兩個線程池串行線程池sDefaultExecutor和線程池THREAD_POOL_EXECUTOR。sDefaultExecutor用于任務(wù)的排隊扑馁,THREAD_POOL_EXECUTOR真正的執(zhí)行任務(wù)涯呻。線程的切換使用Handler(InternalHandler)實現(xiàn)。
3 問題解答
1)為什么AsyncTask在主線程創(chuàng)建執(zhí)行腻要?
因為AsyncTask需要在主線程創(chuàng)建InternalHandler复罐,以便onProgressUpdate
, onPostExecute
雄家, onCancelled
可以正常更新UI效诅。
2)為什么AsyncTask不適合特別耗時任務(wù)?
AsyncTask實際上是一個線程池趟济。如果有線程長時間占用填帽,且沒有空閑,則其他線程只能處于等待狀態(tài)咙好,會造成阻塞篡腌。
3)AsyncTask內(nèi)存泄漏問題
如果AsyncTask被聲明為Activity的非靜態(tài)的內(nèi)部類,那么AsyncTask會保留一個對創(chuàng)建了AsyncTask的Activity的引用勾效。如果Activity已經(jīng)被銷毀嘹悼,AsyncTask的后臺線程還在執(zhí)行,它將繼續(xù)在內(nèi)存里保留這個引用层宫,導致Activity無法被回收杨伙,引起內(nèi)存泄露。
總結(jié)
通過對AsyncTask源碼分析萌腿,學習了AsyncTask的工作原理限匣,以及在使用AsyncTask的注意事項。希望通過本篇文章的學習可以加深對于AsyncTask的理解毁菱,學習其設(shè)計思想米死。如果您覺得有用锌历,請點贊支持一下,您的支持就是我的動力峦筒。