launcher:点击触发zygote >分配JVM(进程)给应用(应用挂掉,不影响其他引用,独立)>
执行activityThread main函数(初始化环境,创建主线程独有的looper,looper.prepareMainLooper)>
looper.loop
android代码都是handler管理的,线程通信只是一个很小的功能
handler工作机制:handler.sendMessage> handler.sendMessageDelayed >handler.sendMessageAtTime> handler.enqueueMessage(msg.target=this赋值) >messageQueue.equeueMessage 消息放入消息队列(单链表的优先级队列,先进先出,插入顺序是按照时间排序,每插入一个消息和之前消息对比要执行的时间,然后插入)>looper.loop()循环查找要执行的消息>messageQueue.next()取消息 > handler.dispatchMessage()> handlerMessage()
1. 一个线程有几个 Handler?
一个线程可以有若干个Handler,任何地方可以通过new 创建
2.一个线程有几个 Looper?如何保证?
一个线程只能有一个Looper,looper的构造器是private私有的,只有通过Looper.prepare()创建,调用prepare(boolean quitAllowed),默认主线程是不可以quit的,looper持有一个static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); prepare里面先判断sThreadLocal.get()获取looper,如果有,抛出一个线程只能创建一个looper的异常,如果没有,调用new Looper()创建looper,looper构造器里面创建一个Messagequeue,并获取当前线程,创建完成,然后sThreadLocal.set(new Looper())将looper放入到ThreadLocalMap里面存储起来,<sThreadLocal作为key,value>,ThreadLocal线程隔离工具类,维持一个ThreadLocalMap来存储,弱引用,所以ThreadLocalMap<ThreadLocal,value>保证唯一的looper
3.Handler内存泄漏原因? 为什么其他的内部类没有说过有这个问题?
内部类持有外部类对象,handler.enqueueMessage将当前this(handler)传给msg.target,如果是一个延时执行的消息,这导致handler持有activity或者fragment引用,message持有handler,GC无法回收。其他内部类是因为你生命周期一致,不会泄漏
4.为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
因为在主线程中activityThread已经创建了looper,里面调用了prepare 和loop方法,可以直接new,而子线程里面如果创建handler,需要调用looper.prepare和looper.loop
5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
子线程无消息可以调用looper.quit(),然后调用queue.quit给mQuitting赋值为true,queue.next()会根据mQuitting return null结束
6.既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它
内部是如何确保线程安全的?取消息呢?
sychronized内置锁:同一个类实例对象里面其他锁了this的地方,都不能调用,只能等待一个线程只有一个操作messagequeue的地方,保证数据添加,排序插入。取消息也加了锁,因为无法保证,取的时候,是否正在插入数据
7.我们使用 Message 时应该如何创建它?
msg.recyclerUncheck 消息内容全部置空,不会不清除,可以复用,防止重复申请内存,享元设计
避免内存抖动OOM,message.obtain()找是否已经创建过的消息复用
8.Looper死循环为什么不会导致应用卡死?
主线程的MessageQueue在没有消息时,便会阻塞在next()里面的nativePollOnce方法中,此时会释放CPU资源,进入睡眠
当有新消息加入的时候,通过往pipe写入数据唤醒工作,采用的是epoll机制,是一种IO多路复用机制,可以监控多个描述符,当某一个准备就绪,就通知相应的程序读或写操作,即读写是阻塞的,所以大多数looper处于休眠状态,不会消耗大量CPU资源
ANR:主要原因是输入事件无响应,而looper休眠的时候,加入消息,会唤醒的,就不会休眠了
Handler消息同步屏障原理 :
handler消息是按照单链表的优先级队列排序的,消息按照时间插入并执行,如果有某些消息想插队立即执行,比如,UI更新,这个时候就需要用同步屏障提高消息的优先级,同步屏障的原理是:默认加消息equeueMessage里面给target赋值的,而messageQueue.postSyngBarrier() msg.target没有赋值,并设置asynchronous为true,loop轮询的时候会判断target为空,然后走一个do while循环,从里面找异步消息执行,执行完后会调用removeSyncBarrier()移除同步屏障
HandlerThread帮我们做了什么?
内部封装好了,方便使用,主要是异步拿到looper
Thread thread = new Thread(new Runnable() {
Looper looper;
@Override
public void run() {
// Log.d(TAG, "click2: " + Thread.currentThread().getName());
Looper.prepare();
looper =Looper.myLooper();
Looper.loop();
}
public Looper getLooper() {
return looper;
}
});
thread.start();
Handler handler = new Handler(thread.getLooper());
因为是异步的线程,所以不一定能拿到looper,而handlerThread里面getLooper的时候,加了锁,如果looper为空,wait释放锁,run里面加锁的地方创建looper开始执行,执行完毕,notifyAll,然后getLooper锁里面开始执行获取looper,保证异步获取looper的问题