Android 并发第四篇
前言:
本篇主要详解AsyncTask 的源码,关于 AsyncTask 的源码其实有太多人都写过了。这里为什么还要写,
是因为博主在并发系列中写AsyncTask的源码,是想通过从并发的角度去理解AsyncTask 为什么这样设计。
我们可以看到 AsyncTask 其中用到了 之前文章中设计到的 FutureTask, 以及Callable,线程池等等。
我们也可以借此复习一下。
一 AsyncTask 的使用:
代码:
private void requestAsy() {
AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
List<ResponInfo> responInfos = getInfos();
long price = 0;
for(ResponInfo responInfo : responInfos) {
if(responInfo.getName().equals(params[0])) {
price = responInfo.getPrice();
}
}
return String.valueOf(price);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
Log.d(TAG,"Asy : "+s);
}
};
//无参
asyncTask.execute();
//或 有参
asyncTask.execute("BMW");
//或 传入我们自定义的线程池
asyncTask.executeOnExecutor(Executors.newFixedThreadPool(10));
}
private List<ResponInfo> getInfos() {
List<ResponInfo> list = new ArrayList<>();
list.add(new ResponInfo("BMW", 2000));
list.add(new ResponInfo("BYD", 200));
try {
Thread.sleep(3000);
}catch (Exception e){
}
return list;
}
二 源码:
1、 execute() 方法:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute 调用的也是 executeOnExecutor()方法,只不过传入的是代码内的默认线程池 sDefaultExecutor。
sDefaultExecutor 这个参数我们稍后再说,现在只要记得它是一个线程池就好了,可以传入 Runnable,或者 Callable。
2、 executeOnExecutor源码:
@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;
}
当我们调用executeOnExecutor() 方法,将不会用AsyncTask 自己的线程池,而改用我们所传入的线程池。
在源码中还有这样的方法:
/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
设置默认的线程池。
不过在最新的版本中被隐藏了,因为有executeOnExecutor() 方法,就用不上这个方法了。
在 executeOnExecutor() 方法中我们看到其调用了onPreExecute() 方法,所以当我们重写 onPreExecute() 方法后, onPreExecute() 会执行在主
线程之中。
我们会将传入的参数传给 mWorker.mParams 。
然后调用线程池执行 mFuture。mFuture 是 FutureTask 根据第一篇文章中的介绍 mFuture 既是 Runnable, 又是 Future。
所以其可以作为一个任务(Runnable)提交给线程池,也可以作为一个Future,从线程池中获取结果。
3 、看构造方法源码:
public AsyncTask() {
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();
//将结果post出来
return postResult(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);
}
}
};
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorker 本身是一个Callable ,mFuture 是 FutureTask 。FutureTask 在第二篇文章中,有过介绍,其实现了RunnableFuture接口,
而RunnableFuture 接口同时继承了Runnable和Future 。所以FutureTask 可以作为一个任务被提交给线程池。这里FutureTask 包装了 mWorker。
mWorker 才是真正的任务主体。
mWorker以及 mFuture都是在构造函数中初始化的。
mWorker 作为任务主体,被提交到线程池中,其call() 方法将会执行在子线程之中。而在call() 方法中,调用了doInBackground()方法
所以doInBackground()方法将会执行在子线程之中。并且将mWorker.mParams 传给 doInBackground()方法,所以doInBackground()方法
的参数就是我们 在调用 execute() 和 executeOnExecutor()传入的参数,其类型便是我们在 new AsyncTask() 时的三个泛型中第一个。
在执行过耗时任务之后。会调用 postResult() 方法将结果post出来。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
//发送消息
message.sendToTarget();
return result;
}
这里getHandler()方法返回的是InternalHandler。
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;
}
}
}
发送了 MESSAGE_POST_RESULT 的消息后,在handleMessage() 中调用了result.mTask.finish(result.mData[0]);
result 是 new AsyncTaskResult(this, result) 。
而AsyncTaskResult 只是封装了真正的数据,以及当前 AsyncTask 对象。
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
result.mTask.finish(result.mData[0]);
这行代码实际上调用了 AsyncTask 中的finish() 方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
finish() 方法中则调用了 onPostExecute() 方法。这个流程就是典型的,子线程发送消息通知主线程了,所以onPostExecute() 方法会执行在主线程中。
那么说到这里,整个流程差不多快说完了。但是我们还没有介绍 FutureTask 也就是mFuture 到底是用来做什么,为什么要用他呢?
按照之前的罗逻辑完全可以不用 mFuture 来包装 mWorker 。mWorker 就可以直接被提交给线程池来完成任务。
4 、下面是 FuturenTask 部分源码:
//FuturenTask 包装了一个 Callable。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
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 {
//在这里执行了 Callable 的 call() 方法。获取结果。
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);
}
}
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
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()方法。
done();
callable = null; // to reduce footprint
}
在 AsyncTask 的源码中重写了done() 方法,其中调用了 postResultIfNotInvoked() 方法。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
postResultIfNotInvoked() 也会调用 postResult() 方法,但是其不一定会执行。
因为在mWorker 的 call() 方法中已经设置 为 true 了。
mTaskInvoked.set(true);
所以我在这里推测之所以这么写,是防止出现异常,导致出现没有回调结果的情况。在正常的情况下,postResultIfNotInvoked() 方法是不会
调用 postResult() 方法的。
5 、那么为什么要用 FutureTask 呢? (当然这是我认为,可能有错)
下看 AsyncTask 中的其他几个方法:
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return mFuture.get(timeout, unit);
}
当调用这想通过 get() 方法直接获取结果时,可以直接调用 AsyncTask.get() 等待结果的返回。
可以看到 AsyncTask 中的这几个方法,全部都是依托于 FutureTask 实现。当前线程阻塞,等待结果的返回。
这就是我猜测其用 FutureTask 的原因。可以直接获取结果。
6、 下面来看最开始放在一边的默认的线程池 : 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);
}
}
}
可以看到的是 SerialExecutor 只是实现了 Executor 接口,它只是重写了execute() 方法。将Runnable 包装一下,放进了队列之中。
它并不是一个真正的线程池。将Runnable 放入队列中后,调用了 scheduleNext() 方法。
其实在 scheduleNext() 方法中才是真正的调用线程池执行任务。
THREAD_POOL_EXECUTOR:
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
THREAD_POOL_EXECUTOR 才是一个真正的线程池。关于 THREAD_POOL_EXECUTOR 到底是一个什么样的线程池,可以去看第一篇文章,
那里有详细的介绍。
那为什么 AsyncTask 要搞出 SerialExecutor 这么个鬼呢?
仔细研究其逻辑:
第一此调用时,会把Runnable 放到队列中,然后执行 scheduleNext() 方法,在 scheduleNext() 中判断队列是否存在,是否有数值。
有数值则调用线程池执行。如果这个时候又添加了一个任务有是被加到队列中,mActive 不为空,所以不会调用 scheduleNext() 方法执行任务。
所以这个任务会放在队列之中。这时上一个任务执行完了,会再次调用 scheduleNext() 方法,从队列中获取任务,如果有,则执行。
所以废了这么大的劲,其实就是想维护一个每次只能执行一个任务的线程池,所有的任务会依次执行。
当然如何你不想这样,希望每次可以执行很多任务,那就调用 executeOnExecutor() 方法,传入自定义的线程池,或者 THREAD_POOL_EXECUTOR 每次执行
多个任务。
三 总结:
OK , AsyncTask 的源码就分析到这里。其实到最后我们会发现,真正实现并发的其实还是线程池,AsyncTask 只不过是将其封装一下,暴露一些接口以及参数。方便于调用者使用。我觉得更应该值得学习应该是它封装的思想或者说是思路吧,即如何能通过封装,提供更好的使用,满足更多的需求。
四 下一篇
下一篇会介绍 Android 中另一个可以实现异步操作的类:HandlerThread。并且尝试自定义 HandlerThread ,使其用法更加趋于简单。