Handler学习记录总结

这篇博客详细介绍了Android中Handler的工作机制,包括线程间通信、Looper的唯一性、内存泄漏原因、子线程创建Handler的准备、MessageQueue的线程安全以及Looper如何避免应用卡死。此外,还讨论了ANR问题、同步屏障原理以及HandlerThread的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值