记得14那年导师的第一个项目让我有机会了解 Android,那时可以说是需要什么功能就去找相应的资料。针对网络请求这一模块,在 Android 4.0 之后的版本如果在主线程执行网络请求,运行时是会报错的(我当时就遭过)。因为主线程主要是负责更新界面并与用户进行交互,不能执行耗时的操作。针对这个问题,我通过百度找到的解决方案大体就是创建一个线程 Thread,在线程的 run() 方法中去执行网络请求,最后再通过 Handler 发送消息通知 UI 线程来刷新界面。
同年的暑假,我有机会去一家公司实习(公司氛围和同事都很nice)。在实习过程中慢慢的接触了今天的主角 – AsyncTask。那时可没想太多,拿过来就用啊。可能自己的无知,像 AsyncHttpClient 这么鼎鼎有名的网络请求开源库在那时我居然不知道。现在回想起来,无知者,罪过、罪过。
不过今天不是来讨论 AsyncHttpClinet 的,毕竟今天的主角可是 AsyncTask。先看看我们一般使用 AsyncTask 的操作流程:代码块1
public class MyAsyncTask extends AsyncTask<Params, Progress, Result> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Result doInBackground(Params... params) {
// do something
...
return null;
}
@Override
protected void onPostExecute(Result result) {
super.onPostExecute(s);
}
}
new MyAsyncTask().execute(...);
首先我们先是定义一个类 MyAsyncTask 并让其继承 AsyncTask。由于 doInBackground() 方法是抽象的,所以必须重写。当然,我们也会根据需求去重写其他方法,如 onPreExecute()、onPostExecute()、onPreogressUpdate()等。
好了,万事俱备,只欠执行啦 new MyAsyncTask().execute()
。来看看 execute() 方法具体是做了那些事:代码块2
#AsyncTask.java
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
// mStatus 属性看下面定义
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;
}
private volatile Status mStatus = Status.PENDING;
public enum Status {
/**
* Indicates that the task has not been executed yet.
*/
PENDING,
/**
* Indicates that the task is running.
*/
RUNNING,
/**
* Indicates that {@link AsyncTask#onPostExecute} has finished.
*/
FINISHED,
}
可以看到,execute() 方法会跳转到 executeOnExecutor()。补充一下:Status 是一个枚举类,而 mStatus 的默认初始值为 Status.PENDING,表示当前任务是处于待办状态。首先先是对 mStatus 进行判断,如果当前任务不是处于 Status.PENDING,则会报异常。这也是为什么我们不能执行多次 execute() 的原因。
顺利的情况下,mStatus 会被置成运行状态 Status.RUNNING;接着调用 onPreExecute(),我们可以在该方法里进行一些初始化操作。然后就是将我们传递进来的参数存放在 mWorker.mParams
里,看看 mWoeker
是什么玩意吧:代码块3
#AsyncTask.java
private final WorkerRunnable mWorker;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public AsyncTask() {
mWorker = new WorkerRunnable() {
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);
}
};
mFuture = new FutureTask(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 的构造方法中创建了两个对象:mWorker 和 mFuture。源码写得很清楚,mWorker 是一个 WorkerRunnable
#AsyncTask.java
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
我们先理一理吧。在执行 execute() 方法后,最終会调用 sDefaultExecutor.execute()。在这方法里,首先会将我们传递进来的 mFuture 封装到一个 Runnable 对象中,同时再将这个 Runnable 对象将添加到任务队列 mTasks 中。然后判断 mActive 为空时则执行 scheduleNext()
。这个方法做的事就是从队列中取出消息,然后交给 THREAD_POOL_EXECUTOR
去执行。而 THREAD_POOL_EXECUTOR
是一个线程池执行器 ThreadPoolExecutor。让我们一起跳转到其 execute()
方法里:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
这里最主要的还是addWorker() 操作:
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
上面方法做的事情就是将 firstTask 作为参数拿来创建一个 Worker 对象 w,然后通过 w 获得线程实例 thread,最后就是调用这个线程的 start() 方法。这里我们要明确一下:此时 Worker 对象中的 Thread 属性所持有的 Runnable 对象其实是 代码4 中新创建的 Runnable, 而我们真正的任务是在其 run() 方法中,看下面代码:
#AsyncTask.java
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);
}
}
}
运行 Worker.thread.start() 最終会运行上面的 run() 方法。在该 run() 方法中又会调用传递进来的参数 Runnnable 对象的 run()方法,而这个传递进来的参数其实就是 mFuture,可回头看 代码块2。好吧,请允许我先喘口气… 在前面我们已经知道 mFuture 是在 AsyncTask 的构造方法中进行初始化的。现在让我们直接跳转到 mFuture 所属类型 – FutureTask 类的 run() 方法:
#FutureTask.java
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable 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);
}
}
该方法会最終会执行 callable 对象的 call 方法。那这个 callable 是什么时候初始化的呢?还记得在 AsyncTask 的构造起中创建 mFuture 时会传递 mWorker 参数:
public AsyncTask() {
mWorker = new WorkerRunnable() {
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);
}
};
mFuture = new FutureTask(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);
}
}
};
}
这样一下就明白了,执行 callable 的 call() 方法其实就是调用上面 WorkRunnable 类中的 call() 方法。在该方法中看到了熟悉的代码:Result result = doInBackground(mParams);
原来我们的 doInBackground() 方法是在这里被调用的。当后台任务执行完后,会接着调用 postResult()
:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
在这里会向主线程发送一个消息,表示后台任务已经执行完成。具体的消息处理方法如下:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
protected void onProgressUpdate(Progress... values) {
}
消息类型有两种:MESSAGE_POST_RESULT 和 MESSAGE_POST_PROGRESS,分别代表任务执行完毕(正常执行完成或被中断)和任务正在处理。
好了,现在是不是对整个 AsyncTask 的工作流程有个概念啦,其实归根结底还是通过 Thread+Handler,只不过这里引入线程池的概念 ThreadPoolExecutor,这使得任务调度管理和执行效率得以提升。前面我提到了网络开源框架 AsyncHttpClient,里面已经为我们做了很多准备工作了。之后我会将我做项目时基于 AsyncHttpClient 进行二次封装的库分享给大家。尽情期待哈 ^_^