在Android中ui是非線程安全的,更新ui只能在主線程操作,所以我們平時如果遇到子線程更新UI的情況,必須要切換到主線程上之后再執行,Handler消息機制就是一種處理方案,為了更方便的處理異步任務以及更新操作,Android在很早的版本就提供了AsyncTask類供我們處理短耗時的異步任務,方便我們子線程處理耗時任務,主線程更新UI。
AsyncTask其實相當于是Handler和Thread的一個封裝,下面我們來看下它的用法吧!
1.AsyncTask的基本使用
我們來看一個簡單的例子:
class MyAsyncTask extends AsyncTask<String,Integer,Boolean>{
@Override
protected void onPreExecute() {
//在doInBackground之前執行
Log.e("wangkeke","----onPreExecute thread: "+ Thread.currentThread().getName());
}
@Override
protected Boolean doInBackground(String... strings) {
Log.e("wangkeke","----doInBackground thread: "+ Thread.currentThread().getName());
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(500);
//更新進度
publishProgress(i*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
Log.e("wangkeke","----onProgressUpdate當前下載進度:"+values[0]+" thread: "+ Thread.currentThread().getName());
}
@Override
protected void onPostExecute(Boolean aBoolean) {
Log.e("wangkeke","----onPostExecute任務結束 result = "+aBoolean.booleanValue()+" thread: "+ Thread.currentThread().getName());
}
}
要使用AsyncTask處理異步任務必須要是繼承了它的子類,我們創建了MyAsyncTask類,我們必須重寫它的doInBackground方法,當然其他幾個方法也很重要一般也要重寫,幾個關鍵方法的具體含義如下:
a. onPreExecute:任務開始時會先調用它,它在doInBackground之前執行,用于做一些初始化的工作,它運行在主線程。
b. doInBackground:就像它的名字那樣,它用來在子線程中處理耗時操作。
c. onProgressUpdate:用來接收publishProgress()方法傳遞過來的進度,運行在主線程中。
d. onPostExecute:doInBackground方法執行結束后會調用它,它的參數就是doInBackground的返回值
下面我們來運行一下這個任務:
new MyAsyncTask().execute("start");
嗯,代碼很簡單,執行AsyncTask的execute方法就可以了,看下運行結果:
我們可以發現AsyncTask類有三個泛型參數,下面介紹一下三個參數的具體含義,AsyncTask<Params, Progress, Result>,Params參數就是execute方法里傳遞過來的,它的類型是不定長數組,Progress參數代表onProgressUpdate方法的參數類型,Result參數代表onPostExecute方法的參數類型,如果不需要返回類型指定Void即可。
雖然AsyncTask可以用來執行耗時操作,但是官方建議我們最好用于短操作,幾秒鐘之內的操作,如果是長時間運行的任務,建議使用Executor, ThreadPoolExecutor,FutureTask等。
取消AsyncTask任務:我們可以調用cancel(boolean)方法來取消任務,調用了cancel方法之后,我們可以通過判斷isCancel()的返回值是否為true來判斷任務取消是否成功,為了盡快取消任務,可以用它來打破for循環。
幾個注意點:
1.AsyncTask的類必須在UI線程中加載(android 4.1之后會自動處理),創建和調用。
2.不要主動調用onPreExecute,onPostExecute, doInBackground, onProgressUpdate方法。
3.一個AsyncTask對象只能執行一次execute方法,否則會拋出IllegalStateException異常。
4.AsyncTask在1.6之前是串行執行的,1.6之后允許多個任務并行執行,但3.0之后再次改為串行執行任務(為避免并行執行引發的錯誤)。
2.AsyncTask串行和并行
測試一下AsyncTask的串行和并行,看看是不是像我們上面說的那樣,寫個簡單的例子,每個任務睡眠2秒后打印當前時間:
class MyAsyncTask extends AsyncTask<String,Integer,String>{
@Override
protected void onPreExecute() {
}
@Override
protected String doInBackground(String... strings) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return strings[0];
}
@Override
protected void onProgressUpdate(Integer... values) {
}
@Override
protected void onPostExecute(String taskName) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e("wangkeke",taskName+ "任務結束 時間 = "+df.format(new Date()));
}
}
開啟3個任務測試:
new MyAsyncTask().execute("one");
new MyAsyncTask().execute("two");
new MyAsyncTask().execute("three");
我是在Android 4.3系統運行的,運行結果如下,可以看到多任務是串行執行:
本來想啟動個1.6-3.0的模擬器試試的,但是很遺憾,沒有找到版本這么低的模擬器,因為現在Android版本普遍已經是4.0+了,這里就不測試了,感興趣的同學可以自己嘗試下。
3.AsyncTask源碼分析
話不多說,從new MyAsyncTask().execute("start")開始:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
把傳入的params和sDefaultExecutor作為參數,調用了executeOnExecutor方法,sDefaultExecutor是什么呢:
//sDefaultExecutor的定義如下
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//SERIAL_EXECUTOR的定義如下,注意SerialExecutor對象,下面要用到
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
sDefaultExecutor其實就是android中的線程池的概念,接著看executeOnExecutor方法:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//開啟的AsyncTask任務必須是處于等待執行狀態的,如果已經在運行或者已經執行完成,則拋出異常信息
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)");
}
}
//開始執行把狀態置為RUNNING
mStatus = Status.RUNNING;
//之前也說了,onPreExecute會先執行用于一些初始化的操作
onPreExecute();
//把參數保存在了mWorker中,mWorker是一個Callable對象
mWorker.mParams = params;
//這里調用了Executor的execute方法
exec.execute(mFuture);
return this;
}
這段代碼也很簡單,最后調用了Executor的execute方法,并傳遞了一個FutureTask,這個exec就是上面的sDefaultExecutor對象,而上面說了它其實就是SerialExecutor對象,看下SerialExecutor的execute方法:
private static class SerialExecutor implements Executor {
//隊列存儲每次的異步任務
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
//當前正在執行的AsyncTask任務
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);
}
}
}
這里的r就是之前傳遞過來的FutureTask,看下它的定義:
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);
}
}
};
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//mTaskInvoked標識任務是否被執行過,這里置為true
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//開始調用doInBackground執行異步任務
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
上面分析到SerialExecutor的execute方法,內部會調用r.run(),這里的r是futuretask,于是找到futuretask的run方法,發現會執行FutureTask<Result>(mWorker)構造函數中mWorker的call方法,mWorker的定義代碼在上面列出來了,它調用了doInBackground方法,當futuretask的run方法執行完畢后,會回調自身的done方法。
從上面的代碼可以看出done方法中調用了postResultIfNotInvoked,看下它的代碼:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
//此時wasTaskInvoked為true,WorkerRunnable中call方法置為true的
if (!wasTaskInvoked) {
//發送執行結果
postResult(result);
}
}
private Result postResult(Result result) {
//發送Handler消息
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
Handler定義如下:
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
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:
// 調用自身AsyncTask的finish方法
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//接收到MESSAGE_POST_PROGRESS消息,更新進度,回調onProgressUpdate方法
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
//如果已經撤銷調用onCancelled,否則把結果傳遞給onPostExecute。
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
到這里已經執行完畢了,之前doInBackground沒有介紹,現在來看下:
@WorkerThread
protected abstract Result doInBackground(Params... params);
它是個抽象方法,需要我們自己去實現,記得我們更新進度的時候,調用了publishProgress方法,來看下它的代碼:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
超級簡單,直接用Handler發送了一個MESSAGE_POST_PROGRESS的消息,并且把當前AsyncTask的實例和進度值values傳遞給handleMessage,handler接收到MESSAGE_POST_PROGRESS消息后開始回調onProgressUpdate:
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
onProgressUpdate其實是個空方法,我們自己根據業務邏輯實現即可:
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
至此,AsyncTask調用邏輯分析完成。
等等,好像有個方法我們還沒說,那就是scheduleNext方法:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
如果當前沒有任務在執行,就調用scheduleNext執行下一個任務,首先通過判斷mTasks.poll()方法刪除隊列中的第一個元素,并把其返回值(它的返回值其實就是當前要執行的task,先移除隊列,再執行的意思)賦值給mActive,如果獲取到的mActive != null,說明隊列中有任務存在,就交由線程池ThreadPoolExecutor去執行。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;
}
4.AsyncTask如何并行執行?
從上面的分析我們知道,AsyncTask在3.0之后默認是串行的,那么如果我們需要并行執行,該怎么做呢?
其實AsyncTask提供了相關的api,上面我們執行AsyncTask是通過execute方法,它還有另外一個方法executeOnExecutor,就是用來并行執行任務的,下面我們來看下它的基本用法:
class MyAsyncTask extends AsyncTask<String,Integer,String>{
@Override
protected void onPreExecute() {
}
@Override
protected String doInBackground(String... strings) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return strings[0];
}
@Override
protected void onProgressUpdate(Integer... values) {
}
@Override
protected void onPostExecute(String taskName) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.e("wangkeke",taskName+ "任務結束 時間 = "+df.format(new Date()));
}
}
測試的代碼還是上面的代碼,我們通過executeOnExecutor開啟任務:
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"one");
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"two");
new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"three");
運行結果如下:
可以看到,確實并行執行了任務。