[Android] AsyncTask源码解析

常用例子

本文依照以下常用例子展开:

AsyncTask的使用方法太常见了,这里不做详述。值得注意的是AsyncTask有以下用法:

可以通过get()方法来获得AsyncTask的运行结果,这是通过java.util.concurrent的FutureTask类来完成的。AsyncTask类依赖于java.util.concurrent,task的执行和调度都由FutureTask、Executor、LinkedBlockingQueue等类来完成,下文会对其展开介绍。

源码解析

AsyncTask的状态

AsyncTask有三种状态:

对象初始化完毕后为PENDING状态,execute()被执行时转变成RUNNING状态,onPostExecute执行后变成FINISHED状态。execute()方法仅可以在task为PENDING状态下调用,否则会抛出IllegalStateException。

AsyncTask的初始化

AsyncTask有很多静态属性:

其中:

sThreadFactory:供THREAD_POOL_EXECUTOR使用。为每个thread设定自增的name。

sPoolWorkQueue:供THREAD_POOL_EXECUTOR使用。

THREAD_POOL_EXECUTOR:供SerialExecutor使用,task真正执行的场所。

SERIAL_EXECUTOR:默认executor的实现,下文会对其详述。

sHandler:初始化时注入主线程的Looper,接收MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS消息。

其中SerialExecutor的实现如下所示:

代码逻辑如下所示:

  1. 当SerialExecutor对象的execute(runnable)执行时,首先会将runnable对象包装一下,然后放到一个FIFO的队列mTasks当中。如果是首次执行,那么scheduleNext()方法会被调用。
  2. 在scheduleNext()方法中,mTasks中的runnable被取出并赋给mActive变量,然后将mActive放到THREAD_POOL_EXECUTOR中执行。
  3. 在THREAD_POOL_EXECUTOR中调用run()方法,然后再次调用scheduleNext()方法取出下一个runnable。

接下来看一下AsyncTask的初始化方法:

WorkerRunnable的源码如下所示:

可以看到WorkerRunnable继承于Callable,因此可以放到Executor中执行。因此mWorker这个匿名类的call方法会在Executor中调用。
首先将mTaskInvoked设置为true。mTaskInvoked用于检查当前的mWorker是否已经被执行(有可能还没执行就cancel了)。然后设置一下线程优先级,最后调用postResult()方法向sHandler发送MESSAGE_POST_RESULT消息。

接下来看mFuture。mFuture是一个FutureTask对象,可以通过调用mFuture.get()来堵塞式地等待task的执行结果,详情可以参考这里。done()方法会在mWorker结束(无论是cancel还是正常finish)时调用。其中postResultIfNotInvoked()源码如下所示:

如果当前mWorker没有被执行过,则执行postResult()方法。这里保证了无论mWorker是否正常结束,主线程的回调依然会正常执行。

AsyncTask的执行

通过类似调用new DownloadFilesTask().execute(url1, url2, url3);可以执行一个AsyncTask。execute方法的源码如下所示:

代码逻辑见注释。结合前文对mFuture和mWorker的讲解,可以画出AsyncTask的执行流程图:

[Android] Otto源码简析

用例

本文主要按照如下例子展开:

初始化

首先来看看Bus bus = new Bus()这一句,对应的源码如下所示:

默认参数为enforcer = ThreadEnforcer.MAIN,identifier = DEFAULT_IDENTIFIER,handlerFinder = HandlerFinder.ANNOTATED。我们来看看这些参数是什么意思。

ThreadEnforcer

ThreadEnforcer是一个接口,enforce()方法用于检查当前线程是否为指定的线程类型:

不带参数的构造函数bus()使用默认的ThreadEnforcer.MAIN,表示enforce()方法必须在主线程上执行。

identifier

identifier仅为bus的名字,debug用。

handlerFinder

HandlerFinder用于在注册/反注册的时候查找Subscriber和Producer,后文会对其展开源码级别的解析。不带参数的构造函数bus()使用默认的HandlerFinder.ANNOTATED,表示使用注解来进行查找。

除上述以外,bus类还有两个成员变量handlersByType和producersByType:

分别用于通过event的类型(class类型)来查找event handle和event producer。

注册/反注册事件

如下所示,要A成为订阅者订阅AnswerAvailableEvent,只需将其注册到bus,然后使用@Subscribe注解标记回调方法即可。回调方法要求可见性为public,有且仅有一个参数,类型为订阅的event。

@Subscribe

首先看一下@Subscribe注解:

RetentionPolicy.RUNTIME表示它是运行时的注解,ElementType.METHOD表示用于注解方法。

bus.register

再看一下register流程:

总的来说register做了三件事情:触发新的Producer;注册新的event-handler关系;触发旧的Producer。另外有两点要注意一下:

  • 由于在一般使用场景下,发送/处理event远比注册/反注册操作频繁,所以在保证线程安全的情况下,使用CopyOnWriteArraySet作为保存event和handler的容器,可以大大提高效率。
    CopyOnWrite容器在读的时候不会加锁,写的时候先复制一份,写完再替换原容器。如果容器正在写操作时发生了读操作(或者正在读的时候发生了写操作),读操作的对象为容器的快照(snapshot)。
  • 由于register方法没有加锁,所以在3-1中,尽管已经检查了handlers是否存在,但仍需使用putIfAbsent来保存handler。

EventProducer和EventHandler

注意到bus通过HandlerFinder来查找object上的producer和subscriber,接下来看一下HandlerFinder的实现:

其中findAllProducers方法返回某event type对应的EventProducer,findAllSubscribers返回某event type对应的EventHandler集合。先看一下EventProducer和EventHandler。

EventProducer是一个producer方法的包装类,源码如下:

其中produceEvent方法用于获得event。可以看出为什么Otto要求produce函数不能有参数。

与EventProducer类似,EventHandler是一个event handler方法(事件回调)的包装类,源码如下:

其中handleEvent方法用于在object上调用handle方法(事件回调),传入event对象。可以看出为什么Otto要求event handler函数仅能有一个参数。

dispatchProducerResultToHandler

dispatchProducerResultToHandler方法用于将Producer产生的event分发给对应的handler。源码如下所示:

逻辑比较简单,主要是使用了Producer的produceEvent()方法获得event对象后,调用EventHandler的handleEvent()方法。

bus.unregister

Bus类的unregister方法用于解除目标对象和bus之间的关联关系,包括对象上的producer方法,subscriber方法,源码如下所示:

投递事件

一次简单的事件投递操作如下所示:

我们来看一下post方法的源码实现:

注意几点:

  • 发送一个Event时,订阅了Event父类的Subscriber方法也会被调用。
  • 事件会被放到调用者所在线程的队列里依次分发。

下面分点进行详述。

flattenHierarchy

进行post操作时,首先会通过flattenHierarchy方法获得event的父类或者接口:

从上可知flattenHierarchy()通过getClassesFor()利用深度优先遍历导出了concreteClass的所有父类。

Dispatch Queue

通过post方法投递的event首先会放在当前线程所在的Dispatch Queue中,然后依次分发。Bus类有如下成员属性:

eventsToDispatch是一个ThreadLocal对象,通过initialValue()方法,eventsToDispatch每次在新的线程上调用的时候都会生成新的ConcurrentLinkedQueue实例。event是通过enqueueEvent(event, wrapper)方法放到queue中的,下面看看enqueueEvent()的实现:

offer()方法会会将EventWithHandler对象放到当前线程的queue的尾部。offer方法和add方法的区别在于,当无法插入(例如空间不够)的情况发生时会发挥false,热不是抛出异常。EventWithHandler类对event和handler的关系进行了简单的包装,实现如下:

接下来看看dispatchQueuedEvents方法的实现:

值得注意的是,所有subscribe方法抛出的异常都会在这里捕获,捕获到异常以后event分发过程即停止,直到下一次在该线程上调用post为止。

结构图

综上,Otto的总体结构可用下图表示: