玩转Android Framework:Looper/Handler 线程间通信源码分析

787 阅读3分钟

系统版本: Ubuntu 22.04 lts

AOSP分支: android-14.0.0_r28

Looper的工作方式

提到Handler,那么我们就绕不开Looper,在Android Java层,我们会经常看到如下的代码:

Looper.prepare()
Looper.loop()

这段代码,主要是为了阻塞当前线程,等待消息再来唤醒和处理的,他的作用基本类似于:

while(true){
    //Do something
}

那么接下来,我们就来看下它到底是怎么实现的。

我们先看Looper.prepare的源码:

image.png

可以看到,这个静态方法只是创建了一个新的Looper,并且将它设置给sThreadLocal

从上面的代码我们可以得知,sThreadLocal的类型是ThreadLocal<Looper>:

image.png

那么也就意味着,这段代码的意思就是创建了一个Looper并且赋值给当前线程的sThreadLocal,我们再看Looper的构造方法:

image.png

可以看到它主要的作用就是创建了一个新的消息队列,并且把当前线程保存下来。

我们再看Looper.loop的源码:

image.png

可以看到,首先它调用了myLooper方法:

image.png

获取到了当前线程的Looper实例,然后设置了一些值,最后用了一个死循环中调用了loopOnce方法,我们再进入此方法:

image.png

可以看到,最开始调用的是Looper初始化的时候就初始化好的MessageQueuenext方法,我们进入此方法:

image.png

可以看到里面又是一个死循环,并且其中调用了nativePollOnce,这是一个Native方法,我们进入Native层:

image.png

可以看到这里根据Java层传进来的指针恢复出来了一个NativeMessageQueue,并且调用了它的pollOnce方法。

我们首先来看MessageQueuemPtr,可以看到它是在MessageQueue的构造方法里初始化的:

image.png

我们在Native层也可以看到,它指向的就是一个NativeMessageQueue:

image.png

进入刚才提到的NativeMessageQueuepollOnce方法:

image.png

可以看到,它最终调用的是NativeLooperpollOnce,我们再进入此方法:

image.png

再进入pollInner:

image.png

可以看到,最终是调用了epoll_wait,此时线程进入等待状态。

从这里我们就可以知道,我们Java层的Looperloop最终会进入Native层的Looper,然后执行它的pollInner,然后在其中使用epoll机制来等待消息的唤醒。

Handler的工作方式

首先,我们进入Handler的构造方法:

image.png

可以看到,当我们初始化Handler的时候,Handler会保存当前线程的LooperLooper中的MessageQueue,这个Looper,是我们在调用Looperprepare方法的时候创建的。

我们再看sendMessage方法:

image.png

image.png

image.png

image.png

可以看到,这里最终调用的,是我们在初始化的时候就获取到的MessageQueueenqueueMessage,我们进入此方法:

image.png

可以看到,这里会把消息加入队列,并且调用nativeWake方法,我们进入Native层,找到此方法:

image.png

image.png

可以看到这里会调用Native层对应Looperwake:

image.png

最终这里也是利用epoll机制,进行了唤醒。

Looper处理消息

我们再回到Java层的Looper,找到它的loopOnce方法,当我们获取到Message的时候,可以看到在后面的处理中有这样的代码:

image.png

这里调用了MessagetargetdispatchMessage方法,这个target的赋值,我们可以在HandlerenqueueMessage中找到:

image.png

也就是说,这个target指的就是这个Handler自身,我们进入它的dispatchMessage方法:

image.png

可以看到在这里,做了一个判断,如果Messagecallback,那么就会调用对应Messagecallback,如果初始化的时候传进来了Callback,那么执行此CallbackhandleMessage,否则调用自身的handleMessage

那么到这里,我们就知道了为什么Handler可以进行跨线程通信,因为每个Handler在初始化的时候都保存了当前线程的Looper对象,一般这个Looper在Android中都是MainLooper,也就是主线程的Looper,然后让它去处理自身的回调,这就保证了回调一定是执行在主线程的,因为Handler的初始化,一般就发生在主线程。