Android线程间消息传递机制

952 阅读4分钟

Android线程间消息传递机制

一、线程的消息队列的创建原理

1、可以在子线程创建Handler么?

在子线程里创建handler:

Thread {
  Handler()
}.start()

会抛出以下异常:

意为不能在没有调用prepare函数的线程内创建handler。

为什么会抛出这个异常呢,我们去翻下源码。

下面是Handler的构造函数:

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    ....
    // 获取当前线程的Looper对象,此对象保存在ThreadLocal中
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

而looper的创建是在Looper类的prepare方法中创建的:

// 注,有一个参数,含义为是否允许退出
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
// 创建主线程的looper
public static void prepareMainLooper() {
  	// 主线程的looper不允许退出,故传参false
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
      // 主线程looper创建成功后,保存到一个静态变量里,可随时获取
        sMainLooper = myLooper();
    }
}

Looper的构造函数:

private Looper(boolean quitAllowed) {
  	// 创建MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

MessageQueue的构造函数:

MessageQueue(boolean quitAllowed) {
  	// 保存是否允许退出的变量
    mQuitAllowed = quitAllowed;
  	// 真正的初始化在native层去做
    mPtr = nativeInit();
}

线程Thread、Looper、MessageQueue、Handler之间的关系如下图所示:

一个线程里存在一个Looper,Looper是保存在ThreadLocal中的,一个Looper里面保存一个MessageQueue,MessageQueue接收不同的Handler发过来的Message,然后不断轮询其中的Message,如果存在Message,就把Message交给对应的Handler处理,Handler与Message的对应关系由Message当中的一个target属性来确定。

然后继续看MessageQueue的native层的初始化过程:

// native层的初始化方法
jlong android_os_MessageQueue_nativeInt(JNIEnv* env, jclss clazz){
  NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
  return reinterpret_cast<jlong>(naticeMessageQueue);
}
// natvie层的MessageQueue的构造方法
NativeMessageQueue::NativeMessageQueue(){
	mLooper = Looper::getForThread();	// 获取局部缓存
  // 如果局部缓存为空
  // 则创建一个natice层的Looper,并放到局部缓存中
  if(mLooper == NULL){		
    mLooper = new Looper(false);
    Looper::setForThread(mLooper);
  }
}

然后是native层的Looper的构造函数:

Looper::Looper(bool allowNonCallbacks){
	mWakeEventFd = eventfd(0,EFD_NONBLOCK);	// 创建fd
  rebuildEpollLocked();
}

native层的looper是整个消息循环的核心部分,在其构造方法中,创建了一个fd,在早起的版本中,此处不是创建fd,而是采用普通的管道,因为管道涉及到内存的两次拷贝,所以后来的版本中采用性能更好的fd,其内部就是一个计数器,避免了两次拷贝。

然后是rebuildEpollLocked()函数,此函数是将上面的fd加入epoll的监听队列中:

void Looper::rebuildEpollLocked(){
  // 创建一个epoll
  mEpollFd = epoll_create(EPOLL_SIZE_HINT); 
  ......
  // 将fd添加到epoll的监听队列中,监听它的读事件
  struct epoll_event eventItem;
  memset(&eventItem,0,sizeof(epoll_event));
  eventItem.events = EPOLLIN;	
  eventItem.data.fd = mWakeEventFd;
  epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeEventFd,&eventItem);
  ......
}

真正的监听是在下方的一个死循环中:

int Looper::pollOnce(int timeoutMills, int* outFd,...){
  for(;;){
    pollInner(timeoutMills);
  }
}
int Looper::pollInner(int timeoutMills){
  struct epoll_event eventItems[EPOLL_MAX_EVENTS];
  // 阻塞方法,等待事件发生
  int eventCount = epoll_wait(mEpollFd, eventItems,...); 
  
  // 如果有时间,就在循环中处理
  for(int i = 0; i < eventCount; i++){
    int fd = eventItems[i].data.fd;
    uint32_t epollEvents = eventItems[i].events;
    if(fd == mWakeEventFd && (epollEvents & EPOLLIN)){
      // 如果事件是之前监听的那个计数器,且是读事件,则取出来消耗掉
      awoken(); // 消耗的方法
    }
  }
}

2、主线程的Looper和子线程的Looper有什么区别?

子线程的Looper是可以退出的,主线程的Looper是不能退出的。

3、Looper和MessageQueue、Handler有什么关系?

java层中,Looper和MessageQueue是一对一的关系,native层中,NativeMessageQueue是包含一个native层Looper的,也是一对一的关系。

4、MessageQueue是怎么创建的?

java层的MessageQueue的构造函数会调用一个native函数去创建native层的NativeMessageQueue,NativeMessageQueue中会创建一个Looper,Looper中会创建一个eventfd, 并创建epoll, 然后添加fd的可读事件到epoll中。

二、消息的循环过程

消息循环就是Looper的loop函数:

public static void loop() {
  	// 拿到当前线程的looper
    final Looper me = myLooper();
    ...
    // 然后拿到looper对应的MessageQueue
    final MessageQueue queue = me.mQueue;
		... 
    for (;;) {
      	// 在循环中,不断地取下一条消息
        Message msg = queue.next(); // might block,如果说队列为空,则阻塞在这里
        if (msg == null) {
            // No message indicates that the message queue is quitting.
          	// 没有消息说明Looper结束了,直接return
            return;
        }
        try {
          	// 使用msg对应的handler,分发message,
            msg.target.dispatchMessage(msg);
        } finally {
            ...
        }
				...
        // 回收message,即重置message状态,并放入对象池(单链表)
        msg.recycleUnchecked();
    }
}

其中,最重要的两步,即取消息和分发消息。

先看取消息:MessageQueue中的next()方法

Message next() {
    ... 
    int nextPollTimeoutMillis = 0;
    for (;;) {
       	... 
        //阻塞在这,等待其他线程发消息过来,或者超过超时时间,被唤醒返回
        nativePollOnce(ptr, nextPollTimeoutMillis); 
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            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) {
                if (now < msg.when) {
                    // 如果消息没准备好,设置超时时间,等消息准备好了再唤醒
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 如果消息准备好了,获取消息
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    // 消息标志为使用
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 如果没有消息,说明队列为空,则超时时间设置为-1,阻塞等待消息来
                nextPollTimeoutMillis = -1;
            }
            .......
    }
}

nativePollOnce:

void android_os_MessageQueue_naticePollOnce(JNIEnv* env, jobject obj,jlong ptr, jint timeoutMillis){
  NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
  nativeMessageQueue->pollOnce(env,obj,timeoutMills);
}
void NativeMessageQueue::pollOnce(JNIEnv* env,jobject pollObj,int timeoutMillis){
  ......
  // 核心,循环
  mLooper->pollOnce(timeoutMillis);
  ......
}

可以看到,最终调用了native层的Looper的pollOnce函数,pollOnce函数最终又调用了其pollInner函数,此函数就是最终的监听消息的方法。

三、消息的发送过程

Handler的sendMessage,senMessageEmpty,sendMessageDelayed,post,postDelay,postAt等等方法,最终都调用到:

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 保存了messageQueue的引用
  	MessageQueue queue = mQueue;
    ...
    // 将message和时间戳信息传入到MQ中
    return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  	// 首先设置了message的target 为this,也就是发送消息的handler
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

然后走到MQ的enqueueMessage:

boolean enqueueMessage(Message msg, long when) {
    ......
    synchronized (this) {
        ......
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            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.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

其中nativeWake,即将消息写入到队列中的底层实现,也就是往native层的looper中的eventfd中写入一个数,然后fd就可以收到此事件。

void android_os_MessageQueue_nativeWake(JNIEnv* env,jclass clazz, jlong ptr){
  NativeMessageQueue* naticeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
  nativeMessageQueue->wake();
}

nativeMessageQueue最终调用到其内部Looper的wake方法:

void Looper::wake(){
  uint64_t inc = 1;
  // 往mWakeEventFd当中写入一个数
  write(mWakeEventFd,&inc,sizeof(uint64_t));
}

总结下,添加消息的过程,就是从java层的MessageQueue的enqueueMessage方法开始,此方法在把消息加入到消息队列中的同时,会把调用消息对应的本地MQ的wake()方法,最终通过本地Looper往一个计数器fd中写入一个数,来通知队列消息的添加,并在本地Looper的循环中监听这个计数器fd,来唤醒上层所调用的获取消息的native方法,并处理。

四、消息的分发过程

消息的分发是在Handler的dispatchMessage中

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      	// 先看msg有没有callback,如果有,则直接调用handleCallback
        handleCallback(msg);
    } else {
      	// 如果msg没有callback,则看有没有全局mCallback
        if (mCallback != null) {
          	// 如果有,则调用全局的handleMessage,之后的逻辑要看此callback的返回值,返回true,直接return;
       			// 只有返回false,才会执行到我们自己复写的那个handleMessage方法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
      	// 我们自己复写的handleMessage方法
        handleMessage(msg);
    }
}