在Android开发中,相信有很多小伙伴都用过AsyncTask,我们在做轻量级的耗时任务并且需要更新UI时,需要将这些任务放到子线程里去执行,这时可以选择AsyncTask来满足我们的需求,这里我们就来分析一下它是如何实现的。由于AsyncTask在不同版本的实现是不同的,这里基于4.4以后版本的源码来分析。
先借用官方的例子:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
然后在使用的时候:
new DownloadFilesTask().execute(url1, url2, url3);
非常的简单,只需要写个类继承AsyncTask,复写需要的方法,传递相关参数即可。
首先,我们来看构造对象AsyncTask时做了哪些事:
public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
* @hide
*/
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);
}
}
};
}
}
可以看到,默认构造函数里调用了this((Looper) null);调用了一个被隐藏的构造函数,传入的Looper为null,这个函数里,就只做了一件事情:初始化mHandler、mWork、mFuture这三个成员变量。至于怎么初始化的,我们暂且先搁置,先来看看这三个到底是何方神圣?
private final Handler mHandler;
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
public interface Callable<V> {
V call() throws Exception;
}
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
Handler是我们熟悉的用于线程间通信的类。WorkerRunnable是Callable的一个抽象子类,里面只有一个Params[]型的变量,用于存储我们传入的参数,还有一个需要复写的call()方法,任务的执行就是在这个方法里面实现的。FutureTask是什么,我们可以看一下官方说明:
FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable a FutureTask can be submitted to an Executor for execution.
简单的说,就是一个Callable 和 Runnable的包装类,是可以被提交到线程池Executor里直接执行的(调用其run方法)。
现在,我们回过头来看看对这三个变量的初始化过程:
由于传入的looper为null,所以mHandler=getMainHandler().
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
可以看出mHandler指向的是一个InternalHandler的单例对象,请注意到构造InternalHandler对象时传入的是Looper.getMainLooper(),这个是主线程的Looper,所以通过此handler发送的消息会存储在主线程的消息队列中,并在主线程中被处理。来看看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;
}
}
}
很简单,就是一个Handler的子类,复写了handleMessage方法,在里面处理了两种任务:
- MESSAGE_POST_PROGRESS 对任务的过程更新处理
- MESSAGE_POST_RESULT 对任务的结果处理
上面的两种消息都会被mHandler发送到UI线程的消息队列中,并在UI现在中被处理,而对应的处理方法就是onProgressUpdate和onPostExecute。
再来看mWorker的初始化:
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;
}
};
直接创建了一个WorkerRunnable的匿名子对象,实现了call() 方法,在该方法中,先将mTaskInvoked.set(true),标记该任务已被执行,接着设置线程的优先级,然后就是回调doInBackground方法,这个方法也就是需要我们自己实现的方法,在开篇官方的例子中,我们可以看到,在这个方法里面,通过不断调用publishProgress方法来对过程进度进行更新,在publishProgress方法里将进度值封装到AsyncTaskResult里,然后通过mHandler发送到UI线程的消息队列,交给我们上面分析的handleMessage里去处理,从而让UI线程去更新界面。
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
当doInBackground方法调用完成后,将返回的结果传入postResult(result)方法中,来看其源码:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
和publishProgress一样,将结果封装到AsyncTaskResult中,然后交给handler去处理,也就是在UI线程中调用我们复写的onPostExecute去处理结果。
最后来看看mFuture的初始化:
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);
}
}
};
也是直接创建了一个FutureTask的匿名子对象,并传入了mWorker,之前我们说FutureTask是对Runnable的包装,所以这里就是对mWorker进行了包装,然后复写了done()方法,在其中调用了postResultIfNotInvoked(get());其中,get()方法获取的是任务完成的结果,也就是doInBackground返回的结果,来看下postResultIfNotInvoked方法的源码:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
首先,获取该任务的执行状态,如果已经执行了,就不执行postResult(result)方法,postResult方法我们刚才已经分析过了,至于这个任务的状态,前面我们在初始化mWorker的call方法中已经设置为true了,而后面经过我们分析可以知道mWorker的call方法先于mFuture的done()方法调用,所以,这里的postResult(result)方法基本执行不到。
到目前为止,我们对AsyncTask的构造方法分析完了。所有这些做的都是初始化内容,任务都还没有开始执行。接下来,我们分析任务的真正执行过程。
当我们调用new DownloadFilesTask().execute(url1, url2, url3)时,才能真正的执行任务,来看一下execute方法源码:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
execute方法里面调用了executeOnExecutor方法,在executeOnExecutor中,首先判断任务的状态,如果任务正在执行或是已经执行完成,则会抛出异常,然后回调我们自己复写的onPreExecute()方法做准备工作(UI线程),接着将我们传递进来的参数保存到mWorker对象的mParams变量中,最后调用exec.execute(mFuture)去执行任务,而这里传入的exec是sDefaultExecutor,因此我们来看一下sDefaultExecutor的初始化:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new 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);
}
}
}
sDefaultExecutor指向了一个SerialExecutor类型的对象,SerialExecutor是Executor的实现类,里面有一个成员变量mActive,指向当前正在执行的任务,同时维护着一个双向队列,存储的是等待执行的任务,可以看到,在execute方法中,先将我们传递进来的mFuture外面再包装一层,这里的包装很有意思,非常巧妙的实现了任务串行执行的效果,这应该是我们学习的地方;包装之后的Runnable对象被添加到这个任务队列中去,接下来,如果是首次执行,则调用scheduleNext()方法,在该方法里,首先从队列中拿出一个任务,并赋值给mActive,如果不为null,则交给线程池THREAD_POOL_EXECUTOR去真正的执行任务,线程池在执行任务的过程中,会先调用包装过的Runnable对象的run方法,在run方法中又调用mFuture的run方法,就像剥壳一样,一层一层的拆开,mFuture的run方法调用后,一个任务就真正的执行完成了,然后继续调用scheduleNext()从mTasks任务队列中获取下一个任务,并继续交给线程池去执行,如此循环。由此可以看出整个任务队列的执行是串行的。
现在我们来看mFuture的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 = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
其中callable就是在构造mFuture时传入的mWorker对象,
可以看出如果mWorker不为null,就会回调mWorker的call方法,
而call方法,我们之前就已经分析过了,里面就是任务的真正执行过程,执行完成之后,将变量ran=true,如果没有异常,就执行set(result)方法,最后在finally里做一些收尾的工作。
来看下set(result)的源码:
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
将outcome指向任务返回的结果,然后调用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()方法,最后将callable置空,断开与mWorker的关联。由此也验证了我们之前的mWorker的call方法先于mFuture 的 done()执行。
好了,AsyncTask的源码就分析到这里,接下来,总结一下AsyncTask的优劣性:
1.这里是基于4.4及以上的版本,在3.0之前的版本中,任务是并行执行的,在3.0以上的版本中,任务都是串行执行的,4.4和3.0-4.4之间的版本区别不大,只是将线程池数量改为了动态的了。所有,开发的时候可能需要对版本做出判断,对于不同版本做一些不同的处理。
2.在Activity中定义AsyncTask导致内存泄漏,由于AsyncTask是Activity的内部类,所以会持有外部的一个引用,如果Activity已经退出,但是AsyncTask还没有执行完毕,那么Activity就无法释放导致内存泄漏。对于这个问题我们可以把AsyncTask定义为静态内部类并且采用弱引用。
3.通过以上分析,我们知道了再3.0以后的手机中AsyncTask时串行执行的,如果有一个任务执行时间过长,会导致后面的任务只能在队列中等待,导致阻塞,所以可以考虑把执行时间较短的任务优先加入。如果有需要我们可以直接使用executeOnExecutor方法,然后直接使用THREAD_POOL_EXECUTOR线程池来执行,这样可以多个线程同时执行,这样可以提高效率。
网友评论