为何looper死循环不会产生ANR
ANR的存在是系统检测某个方法执行时间是否过长,过长超出规定限制,那么便会弹窗提示,防止导致接下来的用户交互得不到及时响应和以及UI刷新不及时。
系统目前规定的ANR有4中情况:
- 对于Service,前台服务必须在20s内执行完成,后台服务必须在200s内执行完成,这里说的执行完成是指onCreate方法
- 对于广播,前台广播需要在10s内结束,后台广播是60s,这里指的是onReceive方法
- 对于ContentProvider,publish必须在10s完成
- 对于用户输入或者屏幕触摸事件,必须在5s内得到回应
回过头来说Looper中的死循环,首先第一层死循环是为了保证APP进程可以一直存活,另外可以处理消息队列中的消息而存在。 looper第二层循环主要是从消息队列中读取消息,这里会通过native方法进行等待。
对于APP进程中的主线程方法逻辑的执行,都是通过发送消息给主线程的looper,由looper分配给对应的handler来执行方法的。 所以这里说的ANR,主要是指handler执行方法时候,满足了上述4个条件,然后系统检测到,就会抛出ANR事件,显示ANR弹窗。
而looper本身的死循环并不会导致ANR,他是为了保活进程而存在。
对于message获取有几种方式,一种可以直接new一个,一种是推荐使用Message的obtain方法获取。 两者区别是:
- obtain方法会维护一个message对象的缓存池,可以复用旧的message对象,减少创建message对象时候的开销
对于Handler中的 IDLEHandler,是什么,有什么用呢?
- 首先可以通过mainLooper.queue.addIdleHandler()来添加IDLEHandler
- 当looper的消息队列,messageQueue中没有其他非IDLEHandler的message需要处理的时候,就会依次触发添加的IDLEHandler来调用其方法进行执行
所以,对于一些闲时需要进行的操作,可以交给IDLEHandler来进行,比如用户发呆时候,UI上给用户一个提示,或者进程空闲时候,保存一下日志等等。
looper原理
handler,looper,message各自说明
- message
对于一个消息队列来讲,这个就是消息封装的对象,通过这个来告诉handler,我们的参数,想要做的事情等, message里还维护一个message缓存池,用于复用旧的message对象,减少创建message对象带来的消耗。
- messageQueue
顾名思义就是消息队列,通过链表方式存储message对象,并且提供方法,可以在合适的时间吐出对应的message对象。该类通过native类来决定什么时候可以返回message对象。
- looper
通过循环,不停的读取message对象,并交给其对应的handler处理对应信息,主要起一个消息调度的职责
- handler
主要用于消耗message对象,供调用者实现其内部方法handleMessage或者传入callback,可以存在多个不同的handle用于处理不同的message。
主要方法及其原理
- looper.prepare
主要是为当前线程实例化looper对象,然后通过ThreadLocal保存到当前线程中,保证每个线程只存在一个looper
- new Handler()
取出当前线程的looper对象,取出对应的消息队列
- handler.sendMessage()
发送消息message给messageQueue,queue保存对象到message链表的对应位置中,如果当前队列中无其他message,那么会唤醒messageQueue.next方法消耗message对象。
- looper.loop
开启死循环,不停的从messageQueue中读取message对象,拿到后,调用其对应的handler对象的handleMessage方法
- messageQueue.next
looper.loop方法循环取message对象所调用的方法,当前不存在message时候,该next方法会调用native方法进行阻塞等待,知道native方法重新被激活,重新查找message对象给looper
- messageQueue.nativePollOnce
该方法是messageQueue中的native方法,主要是做方法阻塞用的,当队列中没有可以执行的message时候,这个方法会阻塞,底层会释放cpu资源,直到下个message对象的到来或者到达下个message的执行时间。
ThreadLocal原理
在looper中,我们还看到有用到 ThreadLocal,这个又是什么原理呢? ThreadLocal主要作用是可以在每个线程中,为目标对象,在该线程中保存一份属于线程的对象,每个线程之间通过ThreadLocal获取的对象是不同的,可以简单理解为: ThreadLocalMap保存了ThreadLocal对象和其对应值的映射,保证对于ThreadLocal可以直接通过其对应对象从ThreadLocalMap中获取到对应线程下的值。
对于ThreadLocal原理如下: