Android中AsyncTask的入門(mén)使用學(xué)習(xí)指南
前言
AsyncTask異步任務(wù),用于執(zhí)行耗時(shí)任務(wù)并在UI線(xiàn)程中更新結(jié)果。
我們都知道,Android UI線(xiàn)程中不能執(zhí)行耗時(shí)的任務(wù),否則就會(huì)出現(xiàn)ANR。對(duì)于耗時(shí)的操作就需要放到子線(xiàn)程中操作,操作完成后需要通知UI線(xiàn)程進(jìn)行更新等操作,這就需要Android的異步消息機(jī)制(創(chuàng)建一個(gè)Message對(duì)象,使用Handler發(fā)送出去,然后在Handler的handleMessage()方法中獲得剛才發(fā)送的Message對(duì)象,然后在這里進(jìn)行UI操作)。
不過(guò)本文要說(shuō)的是AsyncTask,其實(shí)早在Android 1.5版本就引入這個(gè)類(lèi),所以我知道大多數(shù)人對(duì)它的用法都已經(jīng)非常熟悉了?;居梅ㄔ诰W(wǎng)上搜搜就有很多教程,然而,在使用時(shí),仍需要注意其潛在的問(wèn)題以及缺陷。
[TOC]
AsyncTask 簡(jiǎn)單使用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private ProgressDialog mDialog;
private AsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDialog = new ProgressDialog(this);
mDialog.setMax(100);
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setCancelable(false);
mAsyncTask = new MyAsyncTask();
findViewById(R.id.tv).setOnClickListener(this);
}
@Override
public void onClick(View view) {
mAsyncTask.execute();
}
private class MyAsyncTask extends AsyncTask<Void, Integer, Void> {
@Override
protected void onPreExecute() {
mDialog.show();
Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
}
@Override
protected Void doInBackground(Void... params) {
// 模擬數(shù)據(jù)的加載,耗時(shí)的任務(wù)
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
mDialog.setProgress(values[0]);
Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
}
@Override
protected void onPostExecute(Void result) {
// 進(jìn)行數(shù)據(jù)加載完成后的UI操作
mDialog.dismiss();
Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
}
}
}
如以上實(shí)例中,當(dāng)UI線(xiàn)程中需求處理耗時(shí)的操作時(shí),我們可以放在AsyncTask的doInBackground方法中執(zhí)行,這個(gè)抽象的類(lèi),有幾個(gè)方法需要我們重新,除了doInBackground,我們可以在onPreExecute中為這個(gè)耗時(shí)方法進(jìn)行一些預(yù)處理操作,同時(shí)我們?cè)趏nPostExecute中對(duì)UI進(jìn)行更新操作。實(shí)例中的publishProgress對(duì)應(yīng)的回調(diào)是onProgressUpdate,這樣可以實(shí)時(shí)更新UI,提供更好的用戶(hù)體驗(yàn)。
AsyncTask 原理
AsyncTask主要有二個(gè)部分:一個(gè)是與主線(xiàn)的交互,另一個(gè)就是線(xiàn)程的管理調(diào)度。雖然可能多個(gè)AsyncTask的子類(lèi)的實(shí)例,但是AsyncTask的內(nèi)部Handler和ThreadPoolExecutor都是進(jìn)程范圍內(nèi)共享的,其都是static的,也即屬于類(lèi)的,類(lèi)的屬性的作用范圍是CLASSPATH,因?yàn)橐粋€(gè)進(jìn)程一個(gè)VM,所以是AsyncTask控制著進(jìn)程范圍內(nèi)所有的子類(lèi)實(shí)例。
1、與主線(xiàn)程交互
與主線(xiàn)程交互是通過(guò)Handler來(lái)進(jìn)行的,因?yàn)楸疚闹饕接慉syncTask在任務(wù)調(diào)度方面的,所以對(duì)于這部分不做細(xì)致介紹,感興趣的朋友可以繼續(xù)去看AsyncTask的源碼部分。
2、線(xiàn)程任務(wù)的調(diào)度
內(nèi)部會(huì)創(chuàng)建一個(gè)進(jìn)程作用域的線(xiàn)程池來(lái)管理要運(yùn)行的任務(wù),也就就是說(shuō)當(dāng)你調(diào)用了AsyncTask#execute()后,AsyncTask會(huì)把任務(wù)交給線(xiàn)程池,由線(xiàn)程池來(lái)管理創(chuàng)建Thread和運(yùn)行Therad。對(duì)于內(nèi)部的線(xiàn)程池不同版本的Android的實(shí)現(xiàn)方式是不一樣的:
AsyncTask 發(fā)展
接下來(lái)我們先簡(jiǎn)單的了解一下AsyncTask的歷史
首先在android 3.0之前的版本,ThreadPool的限制是5個(gè),線(xiàn)程的并發(fā)量是128個(gè),阻塞隊(duì)列長(zhǎng)度10,也就是說(shuō)超過(guò)138個(gè)則會(huì)拋出異常。因此我們?cè)谑褂玫臅r(shí)候,一定要主要這部分限制,正確的使用。
到了在Android 3.0之后的,也許是Google也意識(shí)到這個(gè)問(wèn)題,對(duì)AsyncTask的API做了調(diào)整:
· execute()提交的任務(wù),按先后順序每次只運(yùn)行一個(gè)也就是說(shuō)它是按提交的次序,每次只啟動(dòng)一個(gè)線(xiàn)程執(zhí)行一個(gè)任務(wù),完成之后再執(zhí)行第二個(gè)任務(wù),也就是相當(dāng)于只有一個(gè)后臺(tái)線(xiàn)程在執(zhí)行所提交的任務(wù)(Executors.newSingleThreadPool() )。
· 新增了接口executeOnExecutor()這個(gè)接口允許開(kāi)發(fā)者提供自定義的線(xiàn)程池來(lái)運(yùn)行和調(diào)度Thread,如果你想讓所有的任務(wù)都能并發(fā)同時(shí)運(yùn)行,那就創(chuàng)建一個(gè)沒(méi)有限制的線(xiàn)程池(Executors.newCachedThreadPool() ),并提供給AsyncTask。這樣這個(gè)AsyncTask實(shí)例就有了自己的線(xiàn)程池而不必使用AsyncTask默認(rèn)的。
· 新增了二個(gè)預(yù)定義的線(xiàn)程池SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR。其實(shí)THREAD_POOL_EXECUTOR并不是新增的,之前的就有,只不過(guò)之前(Android 2.3)它是AsyncTask私有的,未公開(kāi)而已。THREAD_POOL_EXECUTOR是一個(gè)corePoolSize為5的線(xiàn)程池,也就是說(shuō)最多只有5個(gè)線(xiàn)程同時(shí)運(yùn)行,超過(guò)5個(gè)的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)就跟2.3版本的AsyncTask.execute()效果是一樣的。而SERIAL_EXECUTOR是新增的,它的作用是保證任務(wù)執(zhí)行的順序,也就是它可以保證提交的任務(wù)確實(shí)是按照先后順序執(zhí)行的。它的內(nèi)部有一個(gè)隊(duì)列用來(lái)保存所提交的任務(wù),保證當(dāng)前只運(yùn)行一個(gè),這樣就可以保證任務(wù)是完全按照順序執(zhí)行的,默認(rèn)的execute()使用的就是這個(gè),也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)與execute()是一樣的。
AsyncTask 源碼簡(jiǎn)析
這里我們從AsyncTask的起點(diǎn)開(kāi)始分析,主要有 execute() 、executeOnExecutor() 。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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()其實(shí)也是通過(guò)執(zhí)行
executeOnExecutor()方法,只是將其中的Executor設(shè)置為默認(rèn)值。 - 在
executeOnExecutor()中將當(dāng)前AsyncTask的狀態(tài)為RUNNING,上面的switch也可以看出,每個(gè)異步任務(wù)在完成前只能執(zhí)行一次。 - 接下來(lái)就執(zhí)行了
onPreExecute(),當(dāng)前依然在UI線(xiàn)程,所以我們可以在其中做一些準(zhǔn)備工作。 - 將我們傳入的參數(shù)賦值給了
mWorker.mParams - 最后
exec.execute(mFuture)
相信大家對(duì)代碼中出現(xiàn)的mWorker,以及mFuture都會(huì)有些困惑。接下來(lái)我們來(lái)看看mWorker找到這個(gè)類(lèi):
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
可以看到是Callable的子類(lèi),且包含一個(gè)mParams用于保存我們傳入的參數(shù),下面看初始化mWorker的代碼:
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));
}
};
//...
}
可以看到mWorker在構(gòu)造方法中完成了初始化,并且因?yàn)槭且粋€(gè)抽象類(lèi),在這里new了一個(gè)實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)了call方法,call方法中設(shè)置mTaskInvoked=true,且最終調(diào)用doInBackground(mParams)方法,并返回Result值作為參數(shù)給postResult方法.可以看到我們的doInBackground出現(xiàn)了,下面繼續(xù)看:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
可以看到postResult中出現(xiàn)了我們熟悉的異步消息機(jī)制,傳遞了一個(gè)消息message, message.what為MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
AsyncTaskResult就是一個(gè)簡(jiǎn)單的攜帶參數(shù)的對(duì)象。
看到這,我相信大家肯定會(huì)想到,在某處肯定存在一個(gè)sHandler,且復(fù)寫(xiě)了其handleMessage方法等待消息的傳入,以及消息的處理。
private static final InternalHandler sHandler = new InternalHandler();
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;
}
}
}
這里出現(xiàn)了我們的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息時(shí),執(zhí)行了result.mTask.finish(result.mData[0]);其實(shí)就是我們的AsyncTask.this.finish(result) ,于是看finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果我們調(diào)用了cancel()則執(zhí)行onCancelled回調(diào);正常執(zhí)行的情況下調(diào)用我們的onPostExecute(result);主要這里的調(diào)用是在handler的handleMessage中,所以是在UI線(xiàn)程中。最后將狀態(tài)置為FINISHED。
mWoker看完了,應(yīng)該到我們的mFuture了,依然實(shí)在構(gòu)造方法中完成mFuture的初始化,將mWorker作為參數(shù),復(fù)寫(xiě)了其done方法。
public AsyncTask() {
...
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);
}
}
};
}
任務(wù)執(zhí)行結(jié)束會(huì)調(diào)用:postResultIfNotInvoked(get());get()表示獲取mWorker的call的返回值,即Result.然后看postResultIfNotInvoked方法
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
如果mTaskInvoked不為true,則執(zhí)行postResult;但是在mWorker初始化時(shí)就已經(jīng)將mTaskInvoked為true,所以一般這個(gè)postResult執(zhí)行不到。好了,到了這里,已經(jīng)介紹完了execute方法中出現(xiàn)了mWorker和mFurture,不過(guò)這里一直是初始化這兩個(gè)對(duì)象的代碼,并沒(méi)有真正的執(zhí)行。下面我們看真正調(diào)用執(zhí)行的地方。execute方法中的:還記得上面的execute中的:exec.execute(mFuture)
exec為executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor
下面看這個(gè)sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
}
}
}
可以看到sDefaultExecutor其實(shí)為SerialExecutor的一個(gè)實(shí)例,其內(nèi)部維持一個(gè)任務(wù)隊(duì)列;直接看其execute(Runnable runnable)方法,將runnable放入mTasks隊(duì)尾;再判斷當(dāng)前mActive是否為空,為空則調(diào)用scheduleNext。方法scheduleNext,則直接取出任務(wù)隊(duì)列中的隊(duì)首任務(wù),如果不為null則傳入THREAD_POOL_EXECUTOR進(jìn)行執(zhí)行。下面看THREAD_POOL_EXECUTOR為何方神圣:
public static final Executor THREAD_POOL_EXECUTOR
=new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到就是一個(gè)自己設(shè)置參數(shù)的線(xiàn)程池,參數(shù)為:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
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 BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
看到這里,大家可能會(huì)認(rèn)為,背后原來(lái)有一個(gè)線(xiàn)程池,且最大支持128的線(xiàn)程并發(fā),加上長(zhǎng)度為10的阻塞隊(duì)列,可能會(huì)覺(jué)得就是在快速調(diào)用138個(gè)以?xún)?nèi)的AsyncTask子類(lèi)的execute方法不會(huì)出現(xiàn)問(wèn)題,而大于138則會(huì)拋出異常。其實(shí)不是這樣的,我們?cè)僮屑?xì)看一下代碼,回顧一下sDefaultExecutor,真正在execute()中調(diào)用的為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);
}
}
}
可以看到,如果此時(shí)有10個(gè)任務(wù)同時(shí)調(diào)用execute(s synchronized)方法,第一個(gè)任務(wù)入隊(duì),然后在mActive = mTasks.poll()) != null被取出,并且賦值給mActivte,然后交給線(xiàn)程池去執(zhí)行。然后第二個(gè)任務(wù)入隊(duì),但是此時(shí)mActive并不為null,并不會(huì)執(zhí)行scheduleNext();所以如果第一個(gè)任務(wù)比較慢,10個(gè)任務(wù)都會(huì)進(jìn)入隊(duì)列等待;真正執(zhí)行下一個(gè)任務(wù)的時(shí)機(jī)是,線(xiàn)程池執(zhí)行完成第一個(gè)任務(wù)以后,調(diào)用Runnable中的finally代碼塊中的scheduleNext,所以雖然內(nèi)部有一個(gè)線(xiàn)程池,其實(shí)調(diào)用的過(guò)程還是線(xiàn)性的。一個(gè)接著一個(gè)的執(zhí)行,相當(dāng)于單線(xiàn)程。
總結(jié):
AsyncTask在并發(fā)執(zhí)行多個(gè)任務(wù)時(shí)發(fā)生異常。其實(shí)還是存在的,在3.0以前的系統(tǒng)中還是會(huì)以支持多線(xiàn)程并發(fā)的方式執(zhí)行,支持并發(fā)數(shù)也是我們上面所計(jì)算的128,阻塞隊(duì)列可以存放10個(gè);也就是同時(shí)執(zhí)行138個(gè)任務(wù)是沒(méi)有問(wèn)題的;而超過(guò)138會(huì)馬上出現(xiàn)java.util.concurrent.RejectedExecutionException;而在在3.0以上包括3.0的系統(tǒng)中會(huì)為單線(xiàn)程執(zhí)行(即我們上面代碼的分析)
好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式
這篇文章主要介紹了Android四種數(shù)據(jù)存儲(chǔ)的應(yīng)用方式的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家理解掌握Android存儲(chǔ)數(shù)據(jù)的方法,需要的朋友可以參考下2017-10-10
Android安卓中循環(huán)錄像并檢測(cè)內(nèi)存卡容量
這篇文章主要介紹了Android安卓中循環(huán)錄像并檢測(cè)內(nèi)存卡容量,當(dāng)內(nèi)存卡空間已滿(mǎn)時(shí),本文還實(shí)現(xiàn)自動(dòng)刪除視頻列表里面的第一個(gè)文件,需要的朋友可以參考下2015-06-06
Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的代碼詳解
小編經(jīng)常遇到這樣的需求類(lèi)似進(jìn)入開(kāi)發(fā)者模式,即多次點(diǎn)擊后執(zhí)行操作。下面小編通過(guò)實(shí)例代碼給大家講解Android實(shí)現(xiàn)連續(xù)點(diǎn)擊多次事件的相關(guān)知識(shí),感興趣的朋友跟隨小編一起學(xué)習(xí)吧2018-10-10
實(shí)例講解Android中ContentProvider組件的使用方法
這篇文章主要介紹了Android中ContentProvider組件的使用方法,包括ContentProvider使用單元測(cè)試的步驟,需要的朋友可以參考下2016-04-04
Android實(shí)現(xiàn)千變?nèi)f化的ViewPager切換動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)千變?nèi)f化的ViewPager切換動(dòng)畫(huà),自定義PageTransformer實(shí)現(xiàn)個(gè)性的切換動(dòng)畫(huà),感興趣的小伙伴們可以參考一下2016-05-05
Android使用PullToRefresh完成ListView下拉刷新和左滑刪除功能
ListView下刷新刷功能相信從事Android開(kāi)發(fā)的猿友們并不陌生,本文就帶領(lǐng)一些剛?cè)腴T(mén)android的朋友或者一起愛(ài)分享的朋友來(lái)簡(jiǎn)單的實(shí)現(xiàn)ListView的下拉刷新和左滑刪除效果。感興趣的朋友一起看看吧2016-11-11
Android?Flutter控件封裝之視頻進(jìn)度條的實(shí)現(xiàn)
這篇文章主要來(lái)和大家分享一個(gè)很簡(jiǎn)單的控制器封裝案例,包含了基本的播放暫停,全屏和退出全屏,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-06-06

