AsyncTask 学习
AsyncTask 类常用于处理 Android 的异步任务。
本文概括AsyncTask 的使用和简单分析内部实现原理。
本文记录基于Android API 27。
要点
- AsyncTask 的创建可以传入 Handler对象或 Looper对象,也可以不传任何对象;
- AsyncTask 需要重写其 doInBackground()方法,此方法会在子线程中执行;
- AsyncTask 的onPreExecute()会在任务正式启动前被调用,此方法会在主线程中执行;
- AsyncTask 的 onPostExecute(Result result) 方法用于执行结果的发送,此方法会在主线程中执行。返回的result是doInBackground()方法的结果,如果任务被取消,此方法不会被调用;
- AsyncTask 内部使用线程池执行后台任务,使用Handler机制传递消息;
- AsyncTask 的实例对象只能被执行一次;
- AsyncTask 对象可以通过调用executeOnExecutor()方法指定其执行的Executor实现类,方便线程池的控制;
分析
构造方法
先看一下AsyncTask的构造方法,构造方法有三个,分别是:
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
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);
}
}
};
}
可以看到,前面两个构造方法都是调用第三个构造方法的。
构造方法中,初始化了三个成员变量mHandler、mWorker和mFuture。
初始化mHandler时,会判断构造方法是否传入null、Handler对象或Looper对象,若有,则当前AsyncTask对象中的mHandler成员是主线程中的Handler对象(参考Handler的分析)。mHandler对象只有在getHandler()方法中被调用:
private Handler getHandler() {
return mHandler;
}
而getHandler()方法只在postResult(Result result)方法中被调用:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在postResult(Result result)中,获得了一个Message对象并用当前的mHandler发送出去。
mWorker和mFuture是线程池相关的两个类的实例。
AsyncTask 的构造方法中的mWorker对象的call()方法,这个方法显示设置了当前任务栈状态,然后设置当前线程优先使用后台线程,调用doInBackground(mParams)方法,这个doInBackground(mParams)方法就是需要自行实现的方法。把doInBackground(mParams)得到的结果赋值给result,最后调用postResult(result)发送出去。这个postResult(result)就是构造方法中的mHandler的发送消息方法。
mFuture对象的done()方法,尝试把FutureTask实例的执行结果拿到,然后调用postResult(result)方法发送出去。在拿result的过程会判断当前任务是否被中断、抛出异常执行异常或被任务被取消,若有,则执行记录、抛出异常或发送空的result。
启动异步任务
构造好一个AsyncTask对象后,一般会调用其execute()方法来启动任务:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
从源码可以看到,这个方法会执行在主线程中。如果在子线程中调用此方法,则子线程也会变成主线程。例如我这么写:
mTvFirst.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
AsyncTask asyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
Log.d(LOG_TAG, "doInBackground当前线程是" + Thread.currentThread().getName());
return null;
}
@Override
protected void onPreExecute() {
Log.d(LOG_TAG, "onPreExecute当前线程是" + Thread.currentThread().getName());
}
};
asyncTask.execute();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(LOG_TAG, "run当前线程是" + Thread.currentThread().getName());
}
}).run();
}
});
点击该TextView后,打印出来的是:
04-17 04:25:32.478 5805-5805/com.erkang.gradlestudy D/EKwong: onPreExecute当前线程是main
04-17 04:25:32.481 5805-5885/com.erkang.gradlestudy D/EKwong: doInBackground当前线程是AsyncTask #1
04-17 04:25:37.479 5805-5805/com.erkang.gradlestudy D/EKwong: run当前线程是main
可以看到,启动AsyncTask的线程也是主线程,尽管是在子线程中调用的。
接下来我们看一下executeOnExecutor(sDefaultExecutor, params)方法:
@MainThread
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;
}
这个方法用了final关键字,目的是不让使用者进行更改。
方法中,先判断AsyncTask当前状态是否准备状态,如果在运行或已结束,则抛出异常。这说明同一个AsyncTask对象不能被启动两次,在运行中状态或已结束状态都不可再次调用。
然后把当前状态设置为运行中,回调onPreExecute()方法。onPreExecute()方法本身为空方法,在主线程中调用,使用者可以重写来实现自己想要的功能。
然后把参数赋值给mWorker,调用Executor的实现类对象sDefaultExecutor的execute(Runnable command)方法。最后把AsyncTask本身返回。
SerialExecutor
这时候,我们看一下这个sDefaultExecutor对象是什么来的,发现在代码里是一个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);
}
}
}
在这个SerialExecutor类中,有一个Runnable的队列mTasks和一个具体的Runnable对象mActive。
在它的execute(final Runnable r)方法中,先把本次execute()方法传进来的Runnable对象和一个scheduleNext()对象组装成一个 新的Runnable对象,放到Runnable的队列mTasks尾部。判断当前mActive是否为空,若为空,则调用scheduleNext()方法。
接下来看scheduleNext()方法,先从mTasks头部取出一个Runnable对象赋值给mActive,若取到的值不为空,则让THREAD_POOL_EXECUTOR执行这个mActive对象。
mActive对象的run方法分两步,第一步是调用传进来的Runnable实现类的run()方法,第二步是继续调用scheduleNext()方法。这是一个把mTasks队列遍历完的意思。刚才我们传进来的Runnable实现类是mFuture对象,那么就是在这里调用mFuture对象的run()方法了。
FutureTask
这时候我们打开mFuture的类FutureTask看一下里面的run()方法的实现:
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> 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);
}
}
开头这个是当前状态是否为NEW和系统的内存判断,finally后面也是一些系统的判断,我们着重看中间try里面的内容好了。
这里会调起FutureTask对象内部的callable成员的call()方法,如果调用成功,会把布尔值ran设置为true。从构造方法我们可以看到,这个callable成员就是AsyncTask中的mWorker对象。也就是线程池THREAD_POOL_EXECUTOR所激活的时候,会调用mWorker的call()方法。
在FutureTask的run()方法中的这个try的步骤后面,会对callable成员是否运行成功做判断,如果为true的话,调用set(result)。继续点进去这个在FutureTask的run()方法中的这个try的步骤后面,会对callable成员是否运行成功做判断,如果为true的话,调用set(result)方法看,对内存状态等进行判断后,调用了finishCompletion()方法。我们看一下这个finishCompletion()方法:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
在这里,我们找到了这个done()方法,也就是在AsyncTask的构造方法中初始化mFuture对象时复写的done()方法了。
看到这里,我们就知道,在AsyncTask构造方法中初始化的两个成员mWorker和mFuture,在某个任务中,是先执行mWorker的call()方法,然后才执行mFuture的done()方法。
InternalHandler
前面将了那么,在各个步骤中调用哪些方法,调用后怎么发送消息,那么,消息被哪个Handler接收呢?接收后怎么处理呢?我们顺着AsyncTask的构造方法,可以找到这个InternalHandler类:
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;
}
}
}
可以看到,这个类会根据消息的类型,通知result去更新进度或结果。
关于
本文为简单的学习笔记,如有错误,请多多指出。
我的GitHub: https://github.com/EKwongChum