系列之三 线程间通信-Handler
系列目录
https://blog.csdn.net/jzlhll123/article/category/7671581
备注:直接跳过了系列2,是因为Binder十分复杂,想要学习后,并总结一些可能比较难。暂时跳过,先分享一些简单的。不过从目前研究的广播机制原理,contentProvider都绕不开binder。所以会尽快给自己压力学习起来!
Handler是android上最常用的线程间通信工具。handler是基于某个thread/loop(主或者次)来给外部调用者去操作的。主要的用途是跨线程调用,操作主线程更新;更重要的是让很多的操作能够排队。android源码framework中就有利用handler实现的状态机
,而且应用到了wifi和蓝牙上。
接下来讲handler的原理。
初始化的时候,会创建一个Looper
和MessageQueue
,如果是自定义的thread,就得手动的开始looper()
。我们看了下HandlerThread
类的源码就知道,主要就是Thread封装了一下looper.perpare()和loop()
。然后loop跑起来以后,就会开启一个死循环。会一直读取MessageQueue的next()
方法,如果next()
里面没消息了,底层让它等待着,达到阻塞线程的目的(当然需要空闲等待)。然后当生产者
(sendMessage的地方很多,这里指的就是架构设计上的对于Handler而言外部整体)sendMessage()的时候,实际就是给MessageQueue队列追加,然后顺便wake
一下我们的消费者
loop线程,让前面的等待继续下去。
提取出来的消息,会有dispatchMessage()
做一些Runnable
或者callback
的判断,一般的情况会给到我们熟悉的handlerMessage()
里面。这个就是应用层,我们需要实现的方法。
所以回顾来看,Handler外任意线程都往一个对列里丢数据;然后,就在一个特殊的线程里面(HandleMessage也在这个线程里),对这个队列操作,或者调整顺序(AtFront等方法)。
一般地,由于使用了非静态的(普通的)内部类或者匿名内部类对象,这种一般出现在Handler和Thread直接new的时候,持有了外部类的引用。而如果在一些延迟操作的时候,生命周期比外部长,容易产生内存泄漏。handler一般使用弱引用等来解决内存泄漏。事实上最关键的,在不需要handler继续处理的时候,需要将handler的msg和callback都移除。
扩展1 底层实现:
这个wait/wake在android上实现是native层是使用epoll + pipe管道实现的。后来android6.0使用了eventfd。如果没有特别的研究的话,我们不需要关注Handler的底层实现机制,我们需要的是一个等待唤醒的模型。为什么不直接用一些java层的等待唤醒比如object的,主要是android是一个系统,cpp代码中也有Handler机制的。所以google建立了一个从上到下的方案。
扩展2 内存泄漏:
为何内部类或者匿名new的对象会产生持有外部引用?
内部类是java编译器,在编译的时候,默认给的构造函数携带了一个外部引用进去。这个内部类我们可以使用外部类的一些方法、变量,而加上static的时候,就提示这些方法和变量错误了;这就是内部类持有了外部类引用的证据。扩展3 framework状态机:
./core/java/com/android/internal/util/StateMachine.java