handler是用looper轮询消息的。
那么我们此次分析的重点就是分析Looper.loop方法。
首先从looper的loop方法开始
因为loop要不断地接受消息。
此时有两种方案
1.一种是死循环不停的去判断有没有消息
2.通过wait/notify方法,让线程阻塞,当子线程发出消息时,调用notify方法通知主线程接收消息
显示,第二种方案效率高得多,looper又是一个常用功能,所以必须做出一个极限的效率优化。
早在Android2.2的时代,loop方法用的是java里object类里的wait方法等待消息的。这里不详细介绍。
在Android2.3以后(目前Android推出8.0),loop的核心实现都用jni藏在c++里,那么接下来我们详细的分析一下c++层
1.首先java层的Looper.loop方法里有一个for(;;),循环执行MessageQueue的next()方法取到消息
2.MessageQueue类的next方法通过调用nativePollOnce方法获取msg
3.android_os_MessageQueue_nativePollOnce方法通过调用了pollOnce方法,我们继续跟踪调用栈
4.pollOnce调用了mLooper的pollOnce方法,我们在跟进。
5.pollOnce调用pollInner方法
6.pollInner方法里面调用了epoll_wait方法,这个方法跟java的wait方法的功能类似,都是让线程阻塞。
到这里我们终于看到了裸漏在外的epoll,
那么epoll是什么呢?
epoll实现了线程阻塞,跟java的wait方法的功能相似。
因为epoll拥有优秀的效率,乃至成为一种轮询时的标准,所以在著名的轮询使用场景中都有epoll方法的身影。
比如包括nginx在内的大多数服务器轮询接受客户端的请求。
我模仿Looper写了一个最简单的epoll使用方式,献上代码,https://github.com/ChinaTengFei/easyLooper
代码很简单,创建了几个子线程一个主线程,子线程通过epoll跟主线程进行通讯。
那么此时我们知道了jni层是如何实现线程的阻塞,我们也知道了轮询的原理。接下来分析下子线程发消息的代码
从Handler.java的sendMessageAtTime开始分析,
~ sendMessageAtTime方法间接调用了MessageQueueen的queueMessage方法
~ 在queueMessage方法中最后有一个if判断,if(needWake)
~ needWake=需要唤醒
~ 正常情况下needWake为true,那么就调用nativeWake方法唤醒主线程
下面是c++层
~ nativeWake调用了NativeMessageQueue类的wake方法
~ wake方法调用了Looper的wake方法:mLooper->wake();
~ wake方法调用write方法写入数据。
~ 此时epoll监听到有数据写入。就把数据返回到java层。
同样写入数据也可以参考我的这个小demo,https://github.com/ChinaTengFei/easyLooper