我们都知道,当一个应用启动的后,通常情况下里面的组件都是属于同一进程,并且只有一个线程——主线程,也叫UI线程。它主要负责相应用户交互以及事件分发、更新用户界面等工作。因此不应该再次线程中进行耗时工作,因为一旦主线程因执行耗时工作甚至阻塞了线程,轻则用户感到卡顿,重则会使系统弹出ANR。因此,所有耗时的工作或者有可能阻塞的工作都应该放在单独的线程中进行,然后将结果显示给用户。
此外,由于Android的UI工具集不是线程安全的,因此UI的更新只能在主线程中进行,这就要求我们的工作线程与主线程进行通信,有多种实现方法,我们可以自己实现主线程与工作线程的通信,但是这样稍显麻烦,Android提供了一种更简单的实现方法:使用AsyncTask。
AsyncTask
对Thread
和Handler
进行了封装,我们只需要继承它并覆写它的onPreExecute, doInBackground(必须), onProgressUpdate 与 onPostExecute
等然后调用execute
方法就可以实现异步处理和UI更新,不用再去关注通信的复杂逻辑了。
事情一下子变得简单了。对于使用,貌似到了这一步也就七七八八了,但是饱暖思淫欲,人清闲了就喜欢搞事儿。玩具总想拆开看看里面,轮子也想拆开看看里面。所以,就去看看源码吧。
事情,还得从子类调用execute
说起。
我们继承AsyncTask
后,需要调用execute
,我们就依次为突破。它的函数实现如下。
//AsyncTask.java
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
逻辑很简单,就是指调用了executeOnExecutor
函数,但从中我们发现一点,就是此函数必须在主线程也就是UI线程中进行调用。我们接着看executeOnExecutor
。
//AsyncTask.java
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
.......
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
为了简洁我们省去了一部分代码,省去的一部分代码是对线程的一些检查。我们发现此函数也必须在主线程中调用,并且,我们发现了我们需要覆写的一个函数onPreExecute()
在此处进行了调用。紧接着就调用到Executor
的execute
,通过追踪它的实际调用者,我们发现他是Executor
的子类——SerialExecutor
,也是AsyncTask
的以及私有内部静态类。它内部维护了一个先进先出的队列mTasks
,里面装着FutureTask
的实力mFuture
,任务到来后 ,会依次被放在队列的末端,之后把队列的第一个元素交由AsyncTask
线程所持有的一个唯一一个线程池实例THREAD_POOL_EXECUTOR
去执行,并在第一个元素任务执行完后取出下一个任务交由THREAD_POOL_EXECUTOR
继续执行,这样就实现了任务的串行执行。
//AsyncTask.java
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);
}
}
}
既然是把任务交给别人处理了,我们就来看看我们创建了啥任务。从上我们知道,我么创建的任务是 nFuture
,那就来看看它长啥样。
通过查看mFuture
的赋值语句mFuture = new FutureTask<Result>(mWorker)
我们发现,它只创建了一个Runnable
的子类FutureTask
的实例并把一个mWorker
传给了它。 我们找到mWorker
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
我们终于有发现一个熟悉的面庞,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;
}
通过进入函数内部发现,它是调用getHandler()
获得一个Handler
的子类InternalHanler
实例用于主函数间惊醒消息传递。InternalHanler
覆写了父类的handleMessage
方法。这个方法是在主线程中调用的,因此对于UI的更新的操作可以再这里进行,我么也发现,我们复习的 onProgressUpdate、onPostExecute
便是在此处得到了调用。
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;
}
}
}
什么?onPostExecute
在哪?在finish
里面。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
Handler
在之前的文章Android源码阅读之——Handler(一)中已经分析过消息传递的主要过程。这里就不在详述了。略微不同的是在这里传递的是简单的消息而不是一个 Runnable
对象,因此最后不是调用Handler
的handleCallback
而是 handlerMessage
。
简单总结一下下。
我们继承AsyncTask
并覆写相应方法后,通过调用execute
方法,执行我们的任务。如果我们覆写了onPreExecute()
方法,主线程会先对其进行调用,然后将doInBackground
交由工作线程进行调用,最后工作线程将结果返还,主线程调用onPostExecute
将结果显示,此外,如果我们覆写了onProgressUpdate
并调用了publishProgress
,主线程会调用onProgressUpdate
进行工作进度的显示。
这样,我们就轻松实现了耗时任务的执行和显示的分离,妈妈再也不用担心主线程因此挂掉了。