handler原理
handler框架是一个安卓的消息处理机制,也可以切换线程传递数据。
handler、looper、message、messageQueue(消息队列)
looper保存在线程中,用Threadlocal保证唯一性。
looper在调用Looper. prepare()初始化的时候,开启一个死循环,轮循是否有新消息要处理。没有消息的时候会调用
message用享元模式,回收后放在复用池中,如果没有已有就新创建,获取对象的方式是调用obtain()。
在把message发送到队列的时候,会绑定一个target就是handler对象,最终在looper所在的线程处理handler的实现方法。
handler在创建时与looper绑定。
Message的存储形式是什么
Message的存储是链表的形式,next相当于链表的尾指针
线程如果阻塞了,需要唤醒线程。为什么会有线程的阻塞呢?
其实MessageQueue内部的消息是按需要发送的时间点从小到大排列的,后面会分析到,从当前if里的when判断也能看出一二,当队首的Message未到达发送的时间点时,说明其当前所有的消息都未到达发送的时间,上面说过,Handler发送消息并不是通过定时器发送的。当队首Message(最近需要发送的Message)未到达发送时间点时,线程被阻塞,所以这里需要根据线程是否阻塞来看是否需要唤醒线程,这样才能使新加入的Message能及时发送出去,不会被阻塞
可以在子线程直接创建一个Handler吗?会出现什么问题,那该怎么做?
不能在子线程直接new一个Handler。因为Handler的工作依赖于Looper,而Looper又是属于某一个线程的,其他线程不能访问,所以在线程中使用Handler时必须要保证当前线程中Looper对象并且启动循环。不然会抛出异常
Looper死循环为什么不会导致应用卡死,会消耗大量资源吗?
对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。真正会卡死主线程的操作是在回调方法onCreate/onStart/onResume等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop本身不会导致应用卡死
-主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在Loop的(messagequeue)queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源