Framework源码分析 Looper Handler

综述

Looper Handler本质上是同一个进程中不同线程间通信的机制,同样也是一个线程间的任务分发机制

Looper Handler

看上面这张图,LooperHandler相关的几个类是Looper,Handler,MessageQueue和Message下面分别简单说明一下这几个类

  • Looper:顾名思义,这个类主要负责循环,一般Looper的处理放在一个线程中,这个线程不断轮询MessageQueue,获取其中的任务进行处理

  • MessageQueue:同样从名字上就可以了解这个类主要是一个Message的队列,负责将Message组织起来,等待Looper的处理

  • Message:Message是一个个任务的实体,里面记录了需要做什么事情,什么时候做等等

  • Handler:Handler的设计十分巧妙,隐藏了背后的Looper,Message和MessageQueue,Handler是唯用户需要打交道的类,统一了发送消息和处理消息的接口。

Looper的初始化

Looper的初始化实在ActivityThread的main函数中,我们分析Activity启动流程中看到过,每一个新应用进程被创建出来后就是从ActivityThread的main函数开始执行的。我们把main函数的代码回顾一下

从上面代码可以看到和Looper相关的代码有两个地方:

  1. Looper.prepareMainLooper()

  2. Looper.loop()

因为Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); 这句代码的判断条件永远是false,所以永远不会执行,这里就先不看了。我们先看Looper.prepareMainLooper()

首先看注释,注释说得很明白,这个prepareMainLooper的作用就是将当前的线程作为主Looper,并且Android framework自己在启动应用时就自己做了,不需要用户自己去调用。在这个方法中,首先调用了prepare方法,然后将myLooper()的返回值保存到sMainLooper中。我们先看下prepare。

prepare首先调用sThreadLocal的get方法,sThreadLocal是一个ThreadLocal变量,ThreadLocal是jvm支持的一种保存各个线程独立数据的机制。在这里sThreadLocal保存的其实就是Looper的实例。get方法在未初始化过之前应该返回null。 之后prepare又调用了sThreadLocal的set方法,新生成一个Looper实例,并保存到sThreadLocal中。我们看下Looper的构造函数

在Looper的构造函数当中,创建了MessageQueue的实例,并将当前现场保存到mThread变量中。那我们接着去看下MessageQueue的构造函数

保存quitAllowed标志,这个标志表明MessageQueue是否允许退出,我们可以看到这个标志是在上面的prepare方法中传下来的,由于ActivityThread是应用的主线程,是不允许退出的,所以这个标志位为false。 然后调用了nativeInit方法,这个方法是一个native方法。

register_android_os_MessageQueue函数将gMessageQueueMethods注册到JVM中,nativeInit在gMessageQueueMethods中定义,对应于android_os_MessageQueue_nativeInit函数

在android_os_MessageQueue_nativeInit函数中创建了一个NativeMessageQueue的实例,并返回了这个实例的指针。我们接着看下NativeMessageQueue的构造函数。

在NativeMessageQueue的构造函数中我们看到这里又建了一个Looper,这里的Looper是一个Native Looper,实际MessageQueue中Message的投递和取出都是通过这个Native Looper来完成的。我们看下这个Native Looper的构造函数。

Native Looper的构造函数中主要是一些初始化工作。

在rebuildEpollLocked函数中,主要是创建一个新的epoll,并将所有需要监听的事件都加入到epoll当中。我们在ActivityThread的main函数中看到,初始化完成后主线程会调用Looper.loop()就进入循环了。那比如触屏之类的UI事件是如何处理的呢?其实就是在这个rebuildEpollLocked函数中都注册到了epoll的监听当中。所以Native Looper处理管理Message的分发外也管理整个应用的事件分发。 到这里Loop.prepareMainLoop()这个方法我们就分析完了。稍微总结下,Loop.prepareMainLoop主要做了几个事情:

  1. Java层Looper的实例化,并放入ThreadLocal这个TLS变量中

  2. MessageQueue的实例化

  3. NativeMessageQueue的实例化

  4. Native层Looper的实例化

下面我们分析下Loop.loop()

我们简单看下loop(), 主体是一个循环,具体的任务分发我们在分发场景再分析

创建和发送任务

要了解创建和发送任务,我们首先需要看下Handler,任务的发送和执行都是通过Handler完成的。我们看下Handler的构造函数

默认构造函数调用Handler的另一个有参构造函数。

调用参数callback为null,async为false。在这个构造函数中保存了Looper和MessageQueue的实例。 用Handler发送消息一般有两种发送,一种是sendMessage,这种发送方式参数是一个Message,并且可以指定延迟,放在队列头等要求;另一种是post一个Runnable。不过这两种方式最终的处理都是一样的。post方式最终Runnable也会被组织成一个Message发送,所以我们就分析第一种方式。我们从获取一个消息开始。

Handler的obtainMessage会调用到Message的obtain方法,并把Handler自己作为参数传进去。我们去看下Message的obtain方法。

Message的obtain方法是一个静态方法,返回的是一个Message实例,如果sPool就是池子里有的话从池子里分配,如果没有则重新创建一个实例。Message的构造方法是一个空方法,所以就不贴代码了。用户获得到一个Message后处理完Message会调用Handler的sendMessage方法族去发送Message。我们随便挑一个方法看下

sendMessage调用了sendMessageDelayed这个方法

sendMessageDelayed调用sendMessageAtTime方法

sendMessageAtTime方法调用enqueueMessage将message加入到MessageQueue队列中

Handler的enqueueMessage调用了MessageQueue的enqueueMessage方法

MessageQueue的enqueueMessage方法主要把Message寻找一个合适的地方插入到消息队列里。如果消息放在了头部则需要唤醒事件队列进行事件的分发。参数mPtr是MessageQueue初始化时保存的NativeMessageQueue的指针。我们看下nativeWake方法,这也是一个native方法。上面我们把nativeWake关联的native函数贴出来了。

android_os_MessageQueue_nativeWake函数调用了NativeMessageQueue的wake方法

NativeMessageQueue的wake方法调用了Native Looper的wake方法

Looper的wake其实是向mWakeEventFd写入一个数字,让epoll进入事件处理。mWakeEventFd本质是一个pipe,epoll监听读端,这里是往pipe的写段写入数据。 到这里Message的创建和入队已经分析完了,我们下面分析Looper.loop()获取事件进行分发

任务的分发和执行

上面提到了消息入队的时候会按照当时情况判断是否唤醒队列的处理。我们这里主要看Looper.loop()的处理过程,我们这里再贴一遍代码。

我们重点看for循环里的内容:

  1. queue.next() 从MessageQueue里获取一个Message

  2. msg.target.dispatchMessage(msg) Message分发处理

  3. msg.recycleUnchecked() Message实例的回收

我们先来看下取Message的过程

首先从函数结构上看,next方法内部是一个for死循环,也就是必须取到一个Message后next才会退出,所以这个方法会阻塞住。for循环外面是一些参数检查,mptr我们再MessageQueue的构造过程中看到过,它保存的是NativeMessageQueue的指针。然后next方法其实从功能上分为两部分:

  1. nativePollOnce,进入Native MessageQueue去处理epoll中获得的事件

  2. 从队列中获取一个合适的Message返回

nativePollOnce也是一个native方法

android_os_MessageQueue_nativePollOnce函数调用的是NativeMessageQueue的pollOnce方法

NativeMessageQueue的pollOnce方法调用了Native Looper的pollOnce方法

调用了另一个pollOnce

由于每次epoll取事件都会取不止一个event,这些event都会先组织起来放在mResponses中等待处理。如果mResponses中为空,则调用pollInner方法

pollInner方法的流程:

  1. 通过epoll_wait获取事件

  2. 处理从epoll_wait中获得的事件,封装成Response放在mResponses中

  3. 处理mMessageEnvelopes中的事件,这些事件是native层通过sendMessage方法发送到Native Looper中来的

  4. 处理mResponses中的事件。

下面看下Message的分发处理。Message的target成员变量其实是一个Handler类型,所以msg.target.dispatchMessage(msg)调用的是Handler的dispatchMessage

进入dispatchMessage分为三条路径去执行:

  1. 如果Message的callback不为null,也就是说之前消息入队是通过Handler的post方法,参数是一个Runnable的执行体

  2. 如果Message的callback为null,也就是之前消息入队是通过sendMessage方法,那需要调用用户定义的Handler中mCallback定义的handleMesage

  3. 如果Handler的mCallback也为null,则会调用Handler的handleMessage方法,不过这个方法是个空实现,如果走到这里就没有任何意义了

handleMessage我们看到的比较多了,正常使用定义Handler回调都是覆写handleMessage方法,下面我们看下Message的handleCallback方法

handleCallback方法就是调用了Message的callback的run方法,callback是Message的一个成员变量,是Runnable类型。 最后是Message实例的回收,msg.recycleUnchecked()是对msg实例的回收,形成一个Message实例的池,由于LooperHandler使用非常频繁,所以用一个分配池方便快速的分配

总结

完整的LooperHandler其实可以从功能上分为上下两层:

  • 上层的是Java层的LooperHandler,主要功能是线程间的消息传递分发

  • 下层的是Native层的LooperHandler,主要负责监听各种硬件,驱动,或者外部的事件触发,主要是通过epoll这个事件机制来完成的

这两层严格来说并不是表里或者一体的关系,从功能上完全可以分开用,不过在Android世界中,由于主线程就是在Looper的循环中,所以就将Native的LooperHandler集成其中了,在每次获取Message的时候都会去Native层查看一下,看是否有事件被触发。

Last updated

Was this helpful?