AsyncTask
、HandlerThrad
、IntentService
都是對Android消息機制的封裝和應用,解決在子線程耗時任務,主線程更新UI的問題。android
AsyncTask是一個抽象類,經過子類繼承重寫doInBackground
,該方法在子線程運行。數組
public class DownloadTask extends AsyncTask<String,Integer,String> {
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
@Override
protected String doInBackground(String... strings) {
publishProgress(1);
return "AsyncTask";
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
複製代碼
AsyncTask<Params, Progress, Result>
的泛型參數,能夠根據業務需求,傳不一樣的類型。第一個參數Params
是指傳到doInBackground
的參數類型,例如瞎子啊進度的URL。第二個參數Progress
是指在執行doInBackground
方法時,經過publishProgress
更新進度狀態的類型,例以下載的進度。第三個參數Result
是doInBackground
方法執行結束後傳到onPostExecute
的參數類型,例以下載結果。bash
相關方法ide
運行在主線程。能夠開始任務以前,對UI或者數據進行初步準備。非必需方法。函數
在子線程(線程池)運行。通常進行耗時操做,例以下載。爲AsyncTask的抽象方法,必須實現。oop
運行在主線程,在doInBackground方法中調用publishProgress方法會回調該方法,顯示當前任務狀態。經常使用來更新下載進度。非必需方法。源碼分析
在主線程運行。在doInBackground方法中return值以後,將回調該方法。非必需方法。post
在主線程運行,任務完成時回調該方法,表示任務結束。ui
開始任務this
task的execute只能調用一次,否則報錯。
DownloadTask task=new DownloadTask();
task.execute("Git");
複製代碼
Android中代碼註釋是必須在UI線程中調用,由於UI線程默認擁有Looper,以及AsyncTask須要更新UI。若是不須要更新UI,在有Looper的子線程均可以建立。
//構造方法1
public AsyncTask() {
this((Looper) null);
}
//構造方法2
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
//構造方法3
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);
}
}
};
}
複製代碼
構造方法一、2,最終調用構造方法3。看看構造方法3作了什麼?
分析一
給mHanlder賦值初始化。先判斷是否傳入Looper對象,若是沒有傳入,或者傳入Looper對象而且和UI線程的Looper相同,則經過調用getMainHandler
方法調用UI線程的Handler對象(這裏能夠簡單理解,AsyncTask在UI線程建立的)。若是不相等,new
一個 Handler
對象。
//AsyncTask
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
//Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
複製代碼
AsyncTask的getMainHandler
方法,經過調用Looper的getMainLooper
方法來得到Looper對象(UI線程的Looper對象)並建立InternalHandler。這裏能夠看到,在沒有傳入Looper的狀況,且不在UI線程建立AsyncTask,會獲取不到Looper對象。sHandler變量是InternalHandler類型,繼承Handler。
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;
}
}
}
複製代碼
在方法處理中,能夠看到是對MESSAGE_POST_RESULT
消息和MESSAGE_POST_PROGRESS
消息的處理,用來在任務結束和進度更新時,切換到主線程,回調相關方法。
分析二
靜態抽象類WorkerRunnable繼承Callable,只多添加了數組mParams,用於保存參數。在WorkerRunnable的call
方法中,主要調用咱們重寫的doInBackground
。最終對調用postResult
方法。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
複製代碼
在分析一中,最後看到InternalHandler對象對MESSAGE_POST_RESULT消息的處理,調用AsyncTask對象的finish
方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製代碼
根據當前AsyncTask的狀態調用onCancelled
或者onPostExecute
方法。
在AsyncTask的構造方法中。經過構造Handler對象,和構造WorkRunnable對象,並將WorkRunnable對象用於建立FutureTask對象。FutureTask實現了RunnableFuture接口,而RunnableFuture接口繼承了Runnable接口和Future接口。因此FutureTask既能作一個Runnable對象直接被Thread執行,也能做爲Future對象用來獲得Callable的計算結果。
DownloadTask task=new DownloadTask();
task.execute("Git");
複製代碼
咱們在UI線程建立DownloadTask對象,並將調用DownloadTask對象的execute
方法,Git
方法是咱們要傳到doInBackground
方法的值;
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
複製代碼
調用AsyncTask的execute方
法會調用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;
}
複製代碼
若是當前AsyncTask對象正在運行或者結束,會拋出異常,一個task只能運行一次。在這裏調用了onPreExecute
方法,並參數賦值給了前面講到的WorkerRunable對象的omParams變量。經過線程池對象exex執行在構造方法建立的FutureTask對象,最終對調用WorkerRunable對象的call
方法,從而執行咱們重寫的doInBackground
方法。
這裏咱們看看線程池對象sDefaultExecutor。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
複製代碼
類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);
}
}
}
複製代碼
經過ArrayDequ保存任務,並以加鎖機制同步execute方法,串行執行任務。
THREAD_POOL_EXECUTO
是什麼東東?
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;
}
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
複製代碼
毫無心外是一個線程池。
經過AsyncTask的構造器的跟蹤,咱們瞭解AsyncTask的實現的總體流程。經過Looper對象建立Handler對象,用於在線程池中發送消息,切換到UI線程,進行相關操做。並建立WorkRunnable對象調用後臺任務(咱們重寫的doInBackground
方法),將WorkRunnable對象傳給FutureTask對象,這樣在線程池中執行時,對結果和過程可控。
在AsyncTask的execute
方法追蹤,咱們知道後臺任務採用雙向隊列保存,線程池經過鎖同步串行運行。
另外須要注意的是,AsyncTask的生命週期和Activity的生命週期並不一致,若AsyncTask持有Activity,容易形成內存泄漏。相似下載耗時操做建議採用IntentServcie
。
知識點分享