Android-消息机制
整体机制
一共有四个角色,Handler消息处理者、Looper消息循环、MessageQueue消息队列、Message消息。当handler调用post或者sendMessage时,最后都会调用内部的sendMessageDelayed方法,再通过enqueueMessage方法,设置了msg.target并将消息加入MessageQueue,在MessageQueue中调用了nativeWake唤醒了next方法中的nativePollOnce。而Looper的loop方法此时因为MessageQueue的next方法被阻塞着,直到next方法返回这条msg,Looper的loop调用了msg.target.diapatchMessage。到达了Handler 的事件分发,进行消息处理。
Thread、Looper、Looper.myLooper,Looper和线程的关系是如何建立关联的,ThreadLocal的实现在其中起了什么作用
从线程中取出myLooper的过程(字节)
Looper.java
有一个static final变量sThreadLocal = new ThreadLocal<Looper>();
Looper.myLooper()是调用了sThreadLocal.get()
ThreadLocal.java
ThreadLocal.get()中获取currentThread,
拿到Thread中的变量ThreadLocalMap,
Thread.java
变量ThreadLocal.ThreadLocalMap,数据被存在了该类中的Entry[] table
ThreadLocal.java
map.getEntry(this)->用和HashMap一样的方式计算下标(hashCode&(table.len-1))
两个变量合流,拿到了ThreadLocalMap.Entry,(Entry是ThreadLocal弱引用和value的键值对组合)
entry.value就是我们需要的Looper
Handler中的delay是如何实现的(字节)
在MessageQueue.next()里,如果头部的这个Message是有延迟而且延迟时间没到的(now < msg.when),会计算一下时间(保存为变量nextPollTimeoutMillis),然后在循环开始的时候判断如果这个Message有延迟,就调用nativePollOnce(ptr, nextPollTimeoutMillis);进行阻塞。nativePollOnce()的作用类似与object.wait(),只不过是使用了Native的方法对这个线程精确时间的唤醒。
postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;
紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;
MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;
Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;
直到阻塞时间到或者下一次有Message进队;
这样,基本上就能保证Handler.postDelayed()发布的消息能在相对精确的时间被传递给Looper进行处理而又不会阻塞队列了。
Handler如果没有消息处理是阻塞的还是非阻塞的(字节)
https://blog.csdn.net/u010126792/article/details/85091348
阻塞的。Looper.loop阻塞在MessageQueue的next方法中,有一个nativePollOnce的native方法,而在MessageQueue的enqueueMessage方法的最后nativeWake方法可以唤醒阻塞,使用了epoll机制
在next()方法内部,如果有阻塞(没有消息了或者只有Delay的消息),会把mBlocked这个变量标记为true,在下一个Message进队时会判断这个message的位置,如果在队首并且时间满足条件,会调用nativeWake()方法唤醒线程!
Handler的post和postDelay的区别(字节)
都是用sendMessageDelayed实现,postDelay设置了delay数值,而post的delay数值为0,接着调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
一个线程可以有几个Looper可以对应几个Handler,同一线程,HandlerA能向HandlerB发送消息吗(字节)
一个线程只能又一个Looper,可以有多个Handler,不能互相发送消息,因为dispatchMessage中通过msg.target记录了发送这个Message的Handler
Looper如何在子线程中创建(字节)
直接使用HandlerThread
Looper.prepare();//Looper初始化
mHandler = new Handler(Looper.myLooper());
Looper.loop();//死循环
线程中的Looper创建
在主线程ActivityThread创建时进行了主线程Looper的初始化,handler依赖于Looper,通过构造函数建立联系,而MessageQueue的实例在Looper中,新建的线程需要我们自己调用Looper.prepare();通过构造函数传入handler,然后用Looper.loop();开启消息机制。Android规定访问UI只能在主线程中进行,否则抛出异常(UI控件不是线程安全的,加锁使逻辑复杂,访问效率降低),通过ViewRootImpl对UI操作做了验证,由checkThread方法完成。
handler.post(Runnable) runnable是如何执行的,handler分发的顺序如何
自己新建了Message并且把Runnable赋值给了Message的callback,在loop中会调用msg.target.dispatchMsg()以一定顺序执行第一顺序就是Message中的callback
- Message中的callback
- 构造函数中传入的callback
- 由其子类重写的handleMessage
handler的Callback和handlemessage都存在,但callback返回true handleMessage还会执行么
不执行,mCallback.handleMessage如果返回true,会return,即使handleMessage被重写了,也轮不到执行了
IdleHandler
https://blog.csdn.net/jdsjlzx/article/details/110532500
Q:IdleHandler 有什么用?
IdleHandler 是 Handler 提供的一种在消息队列空闲时,执行任务的时机;
当 MessageQueue 当前没有立即需要处理的消息时,会执行 IdleHandler;
Q:MessageQueue 提供了 add/remove IdleHandler 的方法,是否需要成对使用?
不是必须;
IdleHandler.queueIdle() 的返回值,可以移除加入 MessageQueue 的 IdleHandler;
Q:当 mIdleHanders 一直不为空时,为什么不会进入死循环?
只有在 pendingIdleHandlerCount 为 -1 时,才会尝试执行 mIdleHander;
pendingIdlehanderCount 在 next() 中初始时为 -1,执行一遍后被置为 0,所以不会重复执行;
Q:是否可以将一些不重要的启动服务,搬移到 IdleHandler 中去处理?
不建议;
IdleHandler 的处理时机不可控,如果 MessageQueue 一直有待处理的消息,那么 IdleHander 的执行时机会很靠后;
Q:IdleHandler 的 queueIdle() 运行在那个线程?
陷进问题,queueIdle() 运行的线程,只和当前 MessageQueue 的 Looper 所在的线程有关;
子线程一样可以构造 Looper,并添加 IdleHandler;