下面我们以一个最简单的例子来研究下Hanlder的实现原理,其中也会涉及到Looper,Message,
MessageQueue,直接上例子:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
private void sendMessageByTPW(){
Message msg = mHandler.obtainMessage();
mHandler.sendMessage(msg);
}
上面这个例子应该是自从改革开放以来最最最最简单的Handler使用场景了,首先是获取一个Message;其次调用Handler的sendMessage方法将这个Message发出去;看起来非常简单,但是实际上是比较复杂的,首先看看new Handler():
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* 这是Handler的默认构造器,他和当前线程(创建Handler的线程)的Looper绑定
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* 如果此线程木有Looper,那么这个Handler就没办法接收Message,所以就抛个异常给你尝尝
*/
public Handler() {
this(null, false);
}
从注释可以看出,每个线程的Handler都有一个Looper对象,Handler在哪个线程创建,就和这个线程的Looper绑定;那么,Looper到底是什么鬼呢?他是Handler机制的动力所在;当Message发往MessageQueue之后,需要在另一侧将这个Message取出来交给Handler处理,就像往水流进水池里,如果没有出水口,这池水就是一潭死水,没有什么价值;Message就好比是水,MessageQueue就是水池,Looper就是抽水泵了,他将池子(MessageQueue)中的水(Message)抽出来,在别的地方(Handler)使用,这样的水(Message)才有价值.正是由于Looper的存在,Message才能一个个进入MessageQueue,然后由从MessageQueue出来,发往不同的handler处理.一个成功的Handler背后,一定有一个伟大的Looper在默默支撑着他.需要注意的是,不是说创建了一个线程,就一定有一个Looper,而是一个线程可以有一个Looper,是可以有,不是必须有;至于到底有没有,主要看你是否需要,对于Android应用的主线程,肯定是有一个Looper的;但是如果一个线程是我们自己创建的子线程,而且在此线程中压根就不需要创建和使用Handler,那么当然就不需要创建Looper了;如果需要Looper的话,就一定要先调用Looper.prepare().下面所有的流程,都以主线程为例进行分析,在子线程中创建Looper,就必须先显式调用Looper.prepare(),但是在主线程就不需要了,这个调用是由系统完成的,具体的调用地方是ActivityThread的main方法.接下来分析下带参数的Handler的构造函数:
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* 使用指定回调接口的当前线程的Looper,设置此Handler是应该异步调用
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Handler默认是同步调用的,除非构造函数里面指定了参数要求异步调用
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
*
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
//如果FIND_POTENTIAL_LEAKS为true的话,就检测此Handler是否可能引起内存泄露
//默认是false,所以不会提醒可能造成内存泄露,具体实现不用管.
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//调用myLooper获取当前线程的Looper
mLooper = Looper.myLooper();
//如果没有拿到Looper对象,也就是没有调用Looper.prepare(),那么死给你看
//所以,在子线程中要拿到Looper对象,就一定要先调用Looper.prepare()
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//每个Looper都有一个MessageQueue和他对于,在这里就拿到MessageQueue
mQueue = mLooper.mQueue;
//callback和async都是传进来的,一个是null,一个是false;前者表示不需要
//回调,后者表示此Handler使用同步方式调用
mCallback = callback;
mAsynchronous = async;
}
此时,系统中对象分布如下: Handler带参数的构造函数看起来不难,但是Looper.myLooper();其实挺复杂的:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*
* 返回和调用myLooper的线程绑定的Looper对象.如果调用线程没和一个Looper
* 绑定(可能是没调用Looper.prepare()),那么就返回null
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myLooper方法看起来简单的掉渣,就是调用sThreadLocal的get方法,其实背后的实现一点都不简单,他涉及到java的一项技术:ThreadLocal.ThreadLocal这项技术在实际应用开发中并不很多,他实现了线程本地存储,具体来说,就是有一个ThreadLocal对象,这个对象可以被多个线程使用,但是每个线程从ThreadLocal拿到的同一类型的变量的属性值是不一样的,也就是这个值是线程级别的,不同线程的值不一样.我们先来看下sThreadLocal的定义:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
一个典型的泛型的使用,可以把ThreadLocal简单的理解成ArrayList那样的容器,里面装的元素就取决于怎么创建ThreadLocal的了,关于ThreadLocal在另一篇文章中分析过:https://www.jianshu.com/p/9ef748d40ef6;在上面的例子中,ThreadLocal装的是Looper对象,那么这些Looper对象是怎么放进去的呢?答案就在上面提到过的prepare方法,不过这个方法只会在子线程里面调用,Looper提供了一个prepareMainLooper方法,专门给主线程往ThreadLocal中添加Looper,prepareMainLooper方法是在ActivityThread方法里面调用的,ActivityThread就是我们常说的主线程,UI线程,但是他不继承自Thread类,在Android应用进程里面,看一个线程是不是主线程,就看他有没有调用prepareMainLooper,我们来看主线程是怎么调用这个方法的:
public final class ActivityThread {
//ActivityThread是Object的子类,不是Thread的子类,切记
//main函数,也就是这个类的入口函数,没什么好说的
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
}
}
下面看下prepareMainLooper的具体实现:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
* 注释很简单,不额外翻译
*/
public static void prepareMainLooper() {
//首先调用prepare,传入一个false,看来,主线程的Looper的
//获取方式和子线程Looper的获取方式没什么两样,区别是一个
//是系统调用,一个是开发者手动调用
prepare(false);
synchronized (Looper.class) {
//如果sMainLooper已经被创建了,那么再次调用prepareMainLooper
//就会抛出异常,所以prepareMainLooper只能调用一次
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//调用myLooper获取一个Looper对象
sMainLooper = myLooper();
}
}
先来看下prepare,看看他到底准备了些什么玩意:
public static void prepare() {
prepare(true);
}
---------------------------------------
private static void prepare(boolean quitAllowed) {
//sThreadLocal.get()就是获取一个Looper对象,如果在prepare的时候
//发现当前线程的Looper对象已经创建,那么就抛出一个异常,也就是说
//,prepare也只能调一次
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//如果当前线程没有Looper对象的话,就创建一个,然后放入sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
先来看下Looper的构造函数:
private Looper(boolean quitAllowed) {
//构建一个MessageQueue,也就是消息队列,我们sendMessage的时候
//,就是将消息发往消息队列的
mQueue = new MessageQueue(quitAllowed);
//获取创建Looper对象的线程
mThread = Thread.currentThread();
}
构建Looper的时候,就构建了一个消息队列MessageQueue,MessageQueue在整个Handler机制中也是很重要的,为了表示对他的尊重,来看下他的定义:
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* MessageQueue是一个低层次的类,他持有由Looper分发的消息的列表。消息并不
* 是直接添加到消息队列的,他是通过很Handler关联的Looper来添加的(从这也可
* 以看出Looper的作用了)
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
* 开发者可以通过Looper的myQueue获取当前线程的消息队列
*/
public final class MessageQueue {
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//一眼就能看出,nativeInit在Native层创建了一个Native层的
//对象,然后mPtr就是这个对象的地址
mPtr = nativeInit();
}
...... //暂不考虑其他的实现
}
在MessageQueue的构造函数里面,通过JNI在Native层创建了一个对象,来看看创建的是什么鬼:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//创建一个NativeMessageQueue对象,从名字都能看出,这是位于Native层
//的消息队列,也就是说,Java层的一个MessageQueue,在Native层有一个
//NativeMessageQueue跟他对应,其实Handler的核心就在Native层,有这样
//一个对象一点都不奇怪
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
//增加对这个NativeMessageQueue对象的引用
nativeMessageQueue->incStrong(env);
//返回这个NativeMessageQueue对象的地址给Java层
return reinterpret_cast<jlong>(nativeMessageQueue);
}
此时,系统中对象分布如下: 再来看下NativeMessageQueue的构造函数:
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
可以看到,创建Native层的NativeMessageQueue的时候,同时创建了一个Native层的Looper对象,这个Looper对象的创建非常重要,一会分析.从上面的代码中可以看出,Native层的Looper对象也是和线程绑定的.
下面瞅瞅Native层Looper的构造函数:
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
//调用eventfd创建一个事件对象,这个对象用来实现进程/线程间的等待/通知机制
//eventfd返回的是一个文件描述符,6.0之前是通过管道的方式来创建的,用管道的
//方式会涉及一个读端和一个写端;6.0开始改成eventfd了,据说是后者要高效点.
//我们知道,消息被添加进消息队列后,要通知Looper,有消息可以获取了,这个通知
//的过程,就靠下面的mWakeEventFd了
mWakeEventFd = eventfd(0, EFD_NONBLOCK);
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno);
AutoMutex _l(mLock);
//重建epoll,很重要的调用
rebuildEpollLocked();
}
Java层的Looper对象在创建的时候,会创建一个Java层的MessageQueue对象,MessageQueue对象又会在Native层创建一个NativeMessageQueue对象,然后将这个对象的地址返回给Java层的MessageQueue对象,NativeMessageQueue和MessageQueue是具有一一对应关系的.NativeMessageQueue对象在创建的过程中,会创建一个Native层的Looper对象,需要注意的是,ativeMessageQueue和MessageQueue具有一一对应关系,但是Java层和Native层的Looper是没有什么关系的,只是类型名字相同而已.Native层的Looper对象在创建的时候,会创建epoll文件句柄,epoll是Linux系统的一种通信机制,在Android中得到了大量的使用,所以简单研究下(可能存在表述不准确的情况,见谅,毕竟不是专业做linux开发的):
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
//如果有老的epoll文件句柄,那么先关闭他
if (mEpollFd >= 0) {
close(mEpollFd);
}
// Allocate the new epoll instance and register the wake pipe.
//首先创建一个epoll专用的文件描述符mEpollFd,epoll_create是
//Linux系统的标准函数,不多分析(也没有能力分析,太菜)
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
//创建并初始化一个事件item,这个eventItem用来表达监听句
//柄的诉求.比如监听哪个句柄,监听这个句柄上的什么事件
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
//我们监听的是EPOLLIN事件,EPOLLIN事件就是指定的文件句柄有数据可读的事件
eventItem.events = EPOLLIN;
//更具体来说,关注的是mWakeEventFd这个文件上的EPOLLIN事件,
//当mWakeEventFd有内容可读时,就唤醒相关线程进行读操作
eventItem.data.fd = mWakeEventFd;
//向epoll注册监听mWakeEventFd这个文件上的eventItem事件,EPOLL_CTL_ADD可以
//理解成往epoll添加监听指定fd的指定的eventItem事件,也可以理解成注册监听
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
//下面这块代码跟Native层的Message机制有关,跟我们分析的handler没什么关系,不管
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d",
request.fd, errno);
}
}
}
到此为止,一个Handler才算是真正创建完成.简简单单的new Handler(),竟然牵扯出这么多知识点.在Java层创建一个Handler,在Native层创建了NativeMessageQueue,Looper,epoll文件句柄等。
Handler创建完后,就可以通过Handler获取一个消息了,下面分析消息是怎么获取的:
public final Message obtainMessage()
{
//调用Message的obtain方法,传入的this是hanlder对象,这个参数很重要
return Message.obtain(this);
}
-------------------------------------------------------
/**
*
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
* 定义一个发送给Handler的包含描述和任意数据对象的消息.这个对象包含了两个int类型
* 和一个Object类类型的成员变量.这些变量并不是每次使用Message的时候需要使用的,其实
* 就是Message的arg1,arg2和obj这三个变量,需要的时候就用,不需要的时候就不用管了
*
* <p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>
*
* 虽然Message的构造函数是公开的,但是获取Message的最好方式是调用Message的obtain方法或者
* Handler的obtainMessage方法,因为这种获取Message的方法将从Message池中拿到一个Message,这
* 种方法比较高效
*
*/
public final class Message implements Parcelable {
//可以看到,Message实现了Parcelable接口,那么每个Message都可以跨进程传输
//可以看到,Handler的obtainMessage方法,最终还是调用Message的obtan方法,注意target = h
//这个赋值很重要,因为不同Handler的Message要分发到各个不同的Handler,不能搞混了
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
}
Message最终是通过obtain来获取的,下面分析obtain方法:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*
* 从全局的(消息)池中获取一个新的Message实例.从消息池中获取消息
* ,很多情况下,能够避免新的对象的创建,估计跟线程池差不多的原理
* ,预先创建一些Message,称之为消息池,当需要获取Message时,从消
* 息池中拿出一个给调用者,池中的消息数量减一;当消息用完后,返回
* 给消息池,这样可以避免频繁创建Message
*/
public static Message obtain() {
//sPoolSync是一个final static类型的Object对象
synchronized (sPoolSync) {
if (sPool != null) {
//消息池是以链表的形式存在,当需要获取新的消息时,将
//链表头返回回去,然后将链表头z
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message的获取过程分析完毕,挺简单的.下面来看下Handler是怎么发送消息的:
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* 将一个Message推送到消息队列的末尾,消息队列是有顺序的,先推送的Message在队列的前面
* 后推送的在队列的后面.这个Message将在Hanlder的handleMessage中被接收.
*
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* sendMessage还有个boolean类型的返回值,不过一般无需关注
*/
public final boolean sendMessage(Message msg)
{
//超简单的调用.这里也可以看出,任何发送消息都属于延时发送,只不过延时的
//时间是多少而已.看来sendMessageDelayed才是隐藏在背后的大佬
return sendMessageDelayed(msg, 0);
}
下面看下消息是怎么延时发送的:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
//延时发送消息可以,但是请不要穿越.
if (delayMillis < 0) {
delayMillis = 0;
}
//简单的调用sendMessageAtTime,把当前时间+延时时间传进去,也就是发送
//消息的真正时间,如果没有延时的话,发送消息的时间就是当前时间,如果
//有延时的话,发送消息的时间就是当前时间+指定的延时时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
下面分析sendMessageAtTime方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//第一个参数是上面创建的消息Message;第二个参数是Message发送的时间
//第一个重要的对象MessageQueue出现
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//调用enqueueMessage讲Message推送到消息队列MessageQueue
return enqueueMessage(queue, msg, uptimeMillis);
}
分析完了sendMessageAtTime了,再来看下Message是入队的:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//target就是一个Handler,也就是说这个Message是哪个Handler发送的,最后将由这个Handler
//来处理这个Message,这也就是target的含义.enqueueMessage是Handler的方法,那么this
//也就是调用enqueueMessage的Handler,最后调用这个Handler的handlerMessage来处理这个Message
msg.target = this;
//mAsynchronous是指此Handler是否是异步Handler,如果是的话,
//那么需要将每个Message设置成异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue的enqueueMessage方法,注意这个queue变量,他是MessageQueue
//类型的,是Handler的成员属性,每个Handler的构造函数里面都会调用mLooper.mQueue
//来初始化他,而mLooper也是在Handler的构造函数里面调用Looper.myLooper()初始化的
//也就是说,一个Handler一旦创建,就会有一个Looper对象和一个MessageQueue对象和他
//绑定在一起
return queue.enqueueMessage(msg, uptimeMillis);
}
下面来分析enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
//第一步就是判断target,也就是没有处理这个Message的
//目标Handlder的话,直接抛出异常,这种情况很少见
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//从方法名来看,如果这个Message正在使用的话,那么抛出异常,后面碰到再分析
//哪样才叫做InUse
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//mQuitting是在quit方法中设置成true的,从名字来看,应该是消息处理完了
//了才调用,处理完了就退出,所以目前为止,mQuitting还是false的
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//markInUse的作用是给Message添加FLAG_IN_USE这个标记位,代表消息队列正在处理这个
//Message了,这个Message已经被使用了,不能往消息队列推送这个消息了
msg.markInUse();
//设置消息发送的时间,when是之前的方法调用传进来的
msg.when = when;
//mMessages是消息队列的头,也就是消息队列第一个消息,代表接下来需要处理的消息
//需要注意的是,mMessages只是指向了消息队列的头,如果需要,他还可以指向别的消息
//p在这里其实就是一个中间变量,就像冒泡排序中引入的中间变量一毛一样
Message p = mMessages;
//是不是需要唤醒线程,因为消息队列一旦没有消息,获取消息的线程就会进入睡眠
//状态,如果有新的消息到来,就需要把睡眠的线程唤醒,让他工作
boolean needWake;
//p == null意味着当前没有消息需要处理,when == 0意味着待处理的消息的发送时间是0,
//0的概念就是立刻,马上处理;when < p.when意味着p是通过sendMessageDelay等方式发送
//的消息,也就是有一个延时,而当前目标消息没有延时,这样的话,还没进入消息队列的消
//息的when值可能比已经在消息队列的消息的when值小;第一种场景非常好理解,消息队列没
//有消息,那么刚发的消息肯定位于队列头,这没毛病;需要注意到的是,消息队列的消息都
//是按照发送时间先后(也就是when值大小)来排序,然后顺序处理的;消息队列只认消息携带
//的when值,至于是否是真的先发送的消息,他其实不关心,已经位于消息队列的消息,在程
//序员看来肯定是先发送的,但是一旦使用延时发送,那么消息队列就会认为这是以后将要发
//送的消息,只是穿越了一下,提前进入了消息队列,尽管提前进入了消息队列,但是消息队
//还是会把它排到后面处理.这个时候如果来了一个没有延时的消息,这个消息就会被插入延
//时消息的前面,典型的插队行为,这就是if分支的作用
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//目标消息要被插入消息队列头部了,马上要处理了,然后将原来的头部
//消息赋值给next,原来的头部消息就等下次处理了
msg.next = p;
//将目标消息赋值给mMessages,代表接下来就处理这个消息
mMessages = msg;
//mBlocked代表了当前的线程状态,如果当前线程是睡眠的话,这个值
//就是true,否则为false。如果线程处于睡眠状态,那么mBlocked就会
//为true,此时就需要唤醒他,所以needWake跟着为true;反之就是false
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//将目标消息插入消息队列中间.通常我们是不需要唤醒事件队列的,除非队列头
//有一个barrier,而且最早的消息是以异步消息加入队列的
//if分支是插队的场景,这里是正常排队的场景,所以要确保这个消息被正确插入
//正确的位置
//是否需要唤醒.需要唤醒的条件有三个:第一个,线程处于休眠状态,这是废话,
//没有睡,哪有醒;p.target == null这个判断需要额外解释,这个方法的第一个步
//就是判断target是不是null,也就是看这个message的Handler是不是null,如果是的
//话,那么就死给你看,流程根本走不到这里.那么这个p怎么可能会出现target为null
//的情况呢?只怪我们太年轻,懂的太少!!!enqueueMessage不是将消息添加进消息
//队列的唯一方法,postSyncBarrier也能将一个消息送入消息队列,不过这种消息是一
//种特殊的消息,他就是一根搅屎棍,因为这种消息是没有相应的Handler处理他的,那
//么这种消息一旦进入消息队列,Looper就拿他没辙了,因为取出来也不知道交给哪个
//Handler处理,这样整个消息队列的就卡在这个消息上了,这种消息叫做栅栏消息.既
//然这个杀千刀的栅栏消息有这样一个功能,那么肯定有移除这个消息的功能,实际上确
//实有,removeSyncBarrier就可以将这根搅屎棍从消息队列里面移除掉,一旦栅栏消息
//被移除,那么栅栏消息后面的消息就会陆续被继续处理.不过栅栏消息就算再叼,他也
//是卡不住异步消息的,也就说说如果栅栏消息后面有一个异步消息的话,这个异步消息
//就会被插入到栅栏消息的前面.所以p.target == null && msg.isAsynchronous()的意
//思是如果当前消息队列的头是栅栏消息,那么后面的消息将会被卡住处理不了;如果此
//时送入消息队列的消息是异步消息,因为栅栏消息是阻挡不了异步消息的步伐的,所以
//此时需要唤醒线程处理这个异步消息,栅栏消息其实就是给消息队列上了一把锁,有时
//候还是蛮有用的
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//前一条消息,也是一个中间变量
Message prev;
//遍历所有的消息,将目标消息的when值和已经在消息队列的的消息的when
//值挨个进行对比,一旦发现一个消息的when值比目标消息的when值大,那
//么目标消息就会被插入这个消息的前面
for (;;) {
//首先将头消息赋值给prev
prev = p;
//其次将头消息的下一条消息赋值给p
p = p.next;
//如果p == null,说明此时消息队列一共就一条消息,那就直接放在消息
//队列的第二位;如果when < p.when,那么说明遍历到的消息是延时发送
//的,那么msg就应该位于这条消息(p)的前面
if (p == null || when < p.when) {
break;
}
//如果需要唤醒,说明消息队列有杀千刀的栅栏消息;如果p是异步消
//息,那么在处理p的时候就会把线程唤醒,那么msg就没必要再去唤醒了
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//for循环完成后,就为目标消息msg找到了正确的位置
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
//如果确实需要唤醒的话,就调用nativeWake唤醒之,ptr上面分析
//过,那就是Native层的MessageQueue的对象的地址.所谓的唤醒
//仅仅是触发下面要分析的nativePollOnce结束等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
下面以一张图说明栅栏消息的特性: 到目前为止,enqueueMessage分析完毕,不算太难,真正难的地方是最后一个调用nativeWake;从上面的分析也可以看出,通过sendMessageDelay等方法发送的消息,并不是延时发送消息,而是指延时处理消息,一旦调用了sendMessageDealy等方法,消息会马上送入消息队列,但不是马上处理,所以延时的是处理,不是发送.下面分析下nativeWake的作用:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
//调用NativeMessageQueue的wake方法
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
//调用Native层Looper的wake方法
mLooper->wake();
}
void Looper::wake() {
uint64_t inc = 1;
//所谓的wake,就是往mWakeEventFd这个文件句柄写入一个1,mWakeEventFd之前分析过
//在创建Native层Looper的时候,通过eventfd创建过一个文件句柄,然后注册到epoll中
//一旦mWakeEventFd一旦被写入内容,就会触发读端去读取mWakeEventFd里面的内容,这
//里写入数据后,epoll就会触发读端读取事件
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
nativeWake的作用就是往mWakeEventFd写入一个数据1,然后epoll就会触发读端去读取相应的事件,至于写的是什么数据,其实在这里并不重要,只要让epoll检测到mWakeEventFd被写入内容即可.Java层通过enqueueMessage发送一个消息后,再通过nativeWake通知epoll,消息队里已经有新的消息了,这样才算是一个完整的发送消息的流程.
消息发出去后,Looper是怎么获取消息,然后交给相应的Handler的handleMessage方法处理呢?这就需要研究Looper的相关代码了.之前我们说过,我们研究的是主线程的Handler机制,那么再回去看下ActivityThread的代码:
public static void main(String[] args) {
......
Looper.prepareMainLooper(); //之前分析过
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//关键在这里
Looper.loop();
}
可以看到,在主线程的main方法里面调用了Looper的loop方法:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*
* 运行这个线程里面的消息队列.当循环结束后,一定要记得调用quit
*/
public static void loop() {
//首先拿到Looper,myLooper的实现比较简单,不额外解释
final Looper me = myLooper();
//如果没拿到Looper的话,那只能死给你看了
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare()
wasn't called on this thread.");
}
//拿到消息队列
final MessageQueue queue = me.mQueue;
......
//死循环,主线程的main方法里面调用了一个死循环的方法.这个死循环非常
//重要;如果没有这个死循环,那么主线程的main方法就很快的退出了,主线
//程的mian方法退出,那么这个应用就over了,所以这个死循环是不可缺少的
for (;;) {
//拿到下一条消息,也就是消息队列的头消息,可能造成阻塞
//,这个调用是重中之重,一会分析
Message msg = queue.next(); // might block
//如果消息队列没有消息了,那么就退出去,整个应用也就over了
//所以一般都不会走到这个分支里面去;正常情况下,要么阻塞在
//next方法里面,要么就是获取到了消息,执行if后面的流程
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//调用Handler的dispatchMessage分发消息,用脚趾头都能想到
//,最后肯定调到了Handler的handleMessage方法
msg.target.dispatchMessage(msg);
.....
//回收消息,然后执行下一次循环
msg.recycleUnchecked();
}
}
loop方法,他的核心就是一个死循环,然后在这个死循环中不断调用next去消息队列里面获取消息;loop本身不难,但是他调用的next方法却不容易,下面分析这个重要的调用:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
//mPtr就是Native层的MessageQueue对象的地址
final long ptr = mPtr;
//如果Native层的对象都没了,那就不用玩了,直接返回空消息
if (ptr == 0) {
return null;
}
//IdleHandler的作用当消息队列当前没有更多消息可以处理时,就做些别的事
//这些别的事就定义在IdleHandler里面,pendingIdleHandlerCount就是这种
//IdleHandler的数量,这事我们不关心
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//下次调用nativePollOnce判断有没有可以处理的消息的时间
int nextPollTimeoutMillis = 0;
//又一个死循环,死循环里面套着死循环
for (;;) {
//不懂
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
}
//判断有没有消息可以处理,这里会造成阻塞,如果消息队列有消息需要处理,那
//么这个方法就会返回,然后执行下面的流程,如果没有的话,就阻塞在这里;
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//mMessages之前分析过,他指向了消息队列的头消息
Message msg = mMessages;
//if分支就是之前分析过的杀千刀的栅栏消息的场景
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//如果碰到栅栏消息了,那么循环判断栅栏消息后面有没有异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//如果头消息的when值比当前的时间大,说明这个消息是延时发送,那么还不到处理
//这个消息的时候,所以先确定什么时候处理这个消息,给nextPollTimeoutMillis
//赋值
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}else {
// Got a message.
//拿到消息要处理了,所以要改变阻塞状态了
mBlocked = false;
//如果msg的前一个消息不为null,那么将前一个消息的next指向
//msg的next;因为msg要被拿出去处理了,这样的话,链表就断开
//了,if分支的赋值能够把链表重新接上
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
//如果prevMsg为null,说明目标消息就位于队列头,那么这个
//消息的下个消息就是队列头了,这就是下面的赋值的作用
mMessages = msg.next;
//将msg的下个消息赋值为null,这样就把msg从队列里面摘了出来
msg.next = null;
//给这个消息添加FLAG_IN_USE标记位
msg.markInUse();
//返回这个消息
return msg;
}
}
}else {
// No more messages.
//如果进入else,说明消息队列没有消息,那么将
//nextPollTimeoutMillis设置成-1,代表无限期阻塞
nextPollTimeoutMillis = -1;
}
//如果要退出Handler的话,那么销毁Native层的MessageQueue
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//如果消息队列是空的,或者消息队列有消息,但是还没到处理的时间
//那么就处理IdleHandler
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果没有IdleHandler,那么就阻塞一下下,将mBlocked置成true
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//初始化一个IdleHandler数组mPendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
//遍历各个IdleHandler,让他们跑起来,不是重点,不做详细分析
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
next也不是太难,真正难的是nativePollOnce,如果nativePollOnce执行完毕了,说明消息队列有消息了,那么就从消息队列里面获取消息;除了使用nativePollOnce方法获知消息队列有没有消息外,其实还有别的方案,比如Java层里面往死里轮询消息队列,看看有没有消息,但是这种方法比较低效,经常做无用功,无端消耗CPU资源,划不来,所以整出nativePollOnce这套机制,从名字也可以看出,在Native层轮询一次.下面分析下这个方法:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
//调用NativeMessageQueue的pollOnce方法
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
----------------------------------
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
//调用Native层Looper的pollOnce
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
----------------------------------
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
//pollOnce也是一个死循环,Handler机制到处可见死循环
for (;;) {
//这个是Native层的Handle,Message相关处理,不管
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
//上面刚申明了result = 0,所以这个分支暂时不进,但是if下面的流
//程可能会改变result的值,如果改变了,就靠if分支退出这个方法了.
if (result != 0) {
//如果下面的pollInner方法返回了,那么先清理一些变量
//接着返回result,这个方法能不能返回,Java层能不能去
//消息队列拿消息,全指望pollInner了,pollInner要争气啊
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
//这才是重点
result = pollInner(timeoutMillis);
}
}
可以看到,Java层调用一次nativePollOnce,Native层就会进入死循环,在死循环里面调用pollInner去做一些操作,如果这个方法返回了,那么下次循环就可以退出pollOnce方法了,如果pollOnce方法退出了,那么Java层的nativePollOnce也就返回了,nativePollOnce方法返回,Java层就可以愉快的去消息队列拿消息了,美滋滋.下面看下pollInner这个重要的方法的实现:
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
//timeoutMillis是Java层传下来的变量,代表阻塞时间;如果为0,说明不阻塞,方法
//调用后立马返回;如果是-1,说明无限阻塞,只有Native层的mWakeEventFd被写入数据
//后才返回;如果大于0,说明在指定的timeoutMillis之间之后就返回
//下面是根据timeoutMillis处理超时时间,也就是说,timeoutMillis秒后,不管
//mWakeEventFd有没有数据,都返回,这种场景常见于延时消息,也就是当前时间
//不是处理消息的时间,得等等,这个等就是在Native层等
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
// Poll.
//result是最终要返回的结果
int result = POLL_WAKE;
//mResponses和Java层的Handler无关,不管
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
//开始轮询,其实就是等待epoll的通知,不是真的轮询,此时是idle状态
mPolling = true;
//构建一个事件的结构体
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//等待注册在mEpollFd上的文件句柄的事件的产生,timeoutMillis是超时时间,整个Handler
//的阻塞,其实就是阻塞在epoll_wait这个方法里面;如果timeoutMillis为0,说明不阻塞,
//立马返回,返回的事件信息就被写在eventItems中,拿到eventItems后,就可以解析里面
//的信息,并返回了;如果timeoutMillis大于0,那么说明过了timeoutMillis之后返回,事
//件信息同样被写在eventItems中;如果timeoutMillis为-1的话,那就是永久阻塞了;那么肯
//定有退出阻塞状态的机制,到底是什么机制呢?就是上面分析过的nativeWake,这个方法在
//Native层会往mWakeEventFd写入一个1,之前分析过;当mWakeEventFd被写入数据后,epoll
//就会检测到,epoll_wait方法就会返回了.epoll使用的是回调,不会占用过多CPU
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
//epoll通知了,退出轮询状态(尽管实际上不是真的轮询,但是变量名取的就是轮询,没办法)
mPolling = false;
// Acquire lock.
//上锁
mLock.lock();
//什么玩意?不懂
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
//如果eventCount小于0,说明内部发生错误,不考虑这种场景
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error, errno=%d", errno);
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
//如果eventCount等于0,说明阻塞超时,不考虑这种场景
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
//正常情况下,会走下面的流程
for (int i = 0; i < eventCount; i++) {
//获取产生此事件的文件句柄
int fd = eventItems[i].data.fd;
//获取此文件句柄上发生的事件类型
uint32_t epollEvents = eventItems[i].events;
//如果发生事件的文件句柄是mWakeEventFd的话,进if分支
//上面分析过,我们注册的就是mWakeEventFd这个文件上的监听
//所以分析if分支即可
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
//醒来
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
...... //who care
}
//搞定
Done: ;
.....
return result;
}
下面看下awoken是怎么执行的:
从上面的分析流程来看,当往消息队列发送一个消息时,就可能会调用nativeWake,这个方法的实现是往mWakeEventFd写入数据,至于是写入什么数据,不重要,重要的是写就可以了;往mWakeEventFd写入数据后的作用就是让MessageQueue的next方法中的nativePollOnce返回,简单来说,nativeWake就是通知MessageQueue有消息来了,赶紧让你家Looper把他取出来拿去处理吧.具体的流程是往mWakeEventFd写入数据后,Looper.cpp的pollInner方法中的epoll_wait就退出了阻塞状态,可以返回了;pollInner返回后,Looper.cpp的pollOnce方法也就返回了(pollOnce方法是一个死循环,肯定需要一个退出,返回机制);pollOnce返回后,Java层的nativePollOnce也就退出阻塞状态返回了,nativePollOnce返回后,MessageQueue的next方法就可以往下走了,下面的流程就是获取消息队列的消息.
当然了,nativeWake可能不会调用,如果不调用的话,说明Java层的Looper正在有条不紊的取出Message并交给相应的Handler处理;这个时候发送一个消息,肯定也会被Looper拿到;只要Looper没有阻塞,消息队列的消息就会被Looper拿到并交给相应的Handler处理;如果Looper阻塞了,那么就调用nativeWake让Looper从阻塞状态醒来.
拿到消息后,必须交给相应的Handler处理,这个流程就靠dispatchMessage方法实现了:
public void dispatchMessage(Message msg) {
//Message的一个回调,如果设置了这个回调,那么调用handleCallback
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果消息没有回调,但是Handler有回调的话,就调用回调的handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//如果没有任何回调的话,直接调用Handler的handleMessage,这个方法在Handler
//是个空实现,这就是为什么我们用Handler的时候要重写handleMessage的原因
handleMessage(msg);
}
}
到这里为止,Handler机制粗略的分析了一遍.整个Handler机制分为Java层和Native层,Java层的流程非常简单;难的是Native层,具体来说是epoll机制,epoll机制是Linux系统的一种机制,对于Android应用开发来说,理解起来有点麻烦,毕竟我们更多的是做应用,跟Java代码打交道,对Linux不输,包括我自己,对于epoll也不理解,有志于成为大神的人可以自己去研究epoll.Native层的作用就是告诉MessageQueue,消息队列的Java层基本分析完毕;Handler将Message送入消息队列后,Looper就不停的从消息队列获取Message,然后调用dispatchMessage,最后将Message分发给相应的Handler处理.从上面可以看出,在获取消息队列的时候,会在nativePollOnce被阻塞住,也就说,如果nativePollOnce没有返回的话,就不会执行下面的获取下一条消息流程;nativePollOnce返回了,就说明消息队列有消息了,Looper就可以拿到这些消息;如果没有返回,就说明消息队列没有消息,或者有消息,但是还不到处理的时间.如果发送一条消息到消息队列,那么怎么使nativePollOnce返回呢?也就是说Looper怎么知道消息队列有消息了?这就要回头看enqueueMessage方法了,之前分析过,这个方法的作用是将一条消息发送到消息队列,发送完了之后,这个方法还做了另一个动作:nativeWake,这个方法就可以将Looper从阻塞状态唤醒,也就是说这个方法可以通知Looper,有新的消息到来了,赶紧喊Looper过来处理.
以上所有流程,本人并不能保证说的全对,限于能力,难免有理解不到位甚至是错误的地方,欢迎指正.