先问几个问题
1.Handler消息处理机制
一个消息处理机制包括三个部分:发送消息,消息循环和处理消息
Handler:发送消息+处理消息
Looper:消息循环
MessageQueue:存储消息的数据结构+分发消息
具体源码不在这里分析。。。
2.ThreadLocal在Handler中的作用
一句话概况:存储线程私有的数据Looper对象
3.每个线程是否可以有多个handler,是否可以有多个Looper
多个handler:可以有多个Handler,他们使用的是同一个Looper对象。
多个Looper:一个线程处理消息时是线性处理的,线程是最基本的执行单位,不会再有并发处理的场景。所以一个线程拥有一个Looper对象,同时也对应一个消息队列。串行的执行着队列中的消息事件。
所以从线程本质来看:线程是没有必要拥有多个Looper对象的。一个Looper对象足矣。
4.消息延迟的底层实现原理
当sendMessage 时,enququeMessage()@MessageQueue 会将消息按照按照实际执行时间顺序(即msg.when)插入到队列
nextPollTimeoutMillis = (int)Math.min(msg.when - now, Interge.MAX_VALUE);
每次会重新计算阻塞的时间,时间到了之后,会唤醒主线程处理消息。
5.Handler导致的内存泄露问题处理
Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。
6.Looper死循环
(1) Looper死循环为什么不会导致应用卡死?
对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder 线程也是采用死循环的方法,通过循环方式不同与 Binder 驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。真正会卡死主线程的操作是在回调方法 onCreate/onStart/onResume 等操作时间过长,会导致掉帧,甚至发生 ANR,looper.loop 本身不会导致应用卡死。
(2) 主线程的死循环一直运行是不是特别消耗 CPU 资源呢?
其实不然,这里就涉及到 Linux pipe/epoll机制,简单说就是在主线程的 MessageQueue 没有消息时,便阻塞在 loop 的 queue.next() 中的 nativePollOnce() 方法里,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作。这里采用的 epoll 机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源
参考:https://www.zhihu.com/question/34652589?sort=created
7.如何保证主线程一直运行,且不一直占用资源
死循环机制+Linux pipe/epoll机制
8.子线程中能更新UI吗
ViewRoot在主线程被创建,如果更新View的代码不在主线程执行的话,那么可能导致View内部绘制等其他逻辑中出现并发问题。 checkThread检查操作View的线程是否是创建View的线程,如上面的实现,在子线程创建的View,也只能在该子线程被访问,若在其他线程 访问,如主线程,仍然会抛出android.view.ViewRootImpl$CalledFromWrongThreadException。参考:
https://www.cnblogs.com/lao-liang/p/5108745.html