# Handler

# 01 怎么实现从子线程发送消息到主线程的?

我们在子线程调用obtain获得一个message对象之后,调用handler的的sendMessage方法,这个方法会调用MessageQueue的enqueueMessage方法把消息放到MessageQueue这个消息队列中。

在应用进程启动的时候,会执行AndroidThread方法,这个方法里面创建了一个Looper对象,调用Looper对象的loop方法,loop中for了一个死循环,这个死循环中调用MessageQueue的next负责取出已经执行时间的message,通过message.target获取到sendMessage时的handler对象,调用这个对象的handMessage方法,我们在new Handler的时候重写了这个方法, 最终消息会被发送到我们重写的这个handMessage方法。

# 02 为什么会内存泄漏

匿名内部类创建handler的时候,handler会持有Activity的引用。在调用handlerMessage的时候,会把当前这个handler赋值给message的target属性。所以message会持有handler的引用。而message被添加到消息队列MessageQueue时消息队列会持有message的引用,消息队列被静态的Loop对象持有。所以最终,looper这个静态对象会持有activity的引用。

引用链:static looper-messageQueue->message->handler->activity

解决这个内存泄漏的方式是原理是把activity从static looper这个GC Root的引用链中移除。可以在创建handler的时候,改为静态的匿名内部类,这样handler就不会持有activity的引用。也可以在activity的destory生命周期中调用handler的removeMessage方法移除message,这样message就不再持有handler。

# 03 消息数量有限制吗

没有限制。因为Android几乎所有的跨线程通信都是通过handler,activity的生命周期,页面的绘制等等。 可能一秒钟会产生几百个message,所以不能也没有数量的限制。

# 04 发送的消息对象应该怎么获取

应该用handler.obtain方法获取。这个方法会从消息复用池pool中取出一个空内容的message,避免了频繁创建对象造成的「内存抖动」。

# 05 延时消息是怎么发送的?

在sendMessageDelayed时,传入延时时间,这个时间会加上当前的时间计算出这个消息应该被取出的时间赋值给消息的when属性。这个when属性就是在Looper对象的loop死循环方法中用来判断是否取出的时间。并且消息队列会根据这个时间来排序消息。当判断当前最新的一条消息没有到消息执行的时间后,就会计算出这个时间差,调用系统的休眠函数,休眠这个这个差后继续轮询消息列表。

# 06 没有消息的时候是阻塞的还是非阻塞的

没有消息的时候是阻塞的。 没有消息的时候,交给native层等待。

# 07 Handler阻塞会造成ANR吗?

不会。 ANR程序未在指定时间响应,本身就是由Handler的延时消息触发的。在执行事件、服务、广播这些操作时,系统会用handle发送一个延时消息,当操作执行完了就移除这个消息,如果在指定的时间操作没有被执行完,这个消息就会被发送,触发ANR。

Handler存在消息则不会进入阻塞状态,不存在消息,那也触发不到ANR。

# 08 怎么在子线程创建Handler?

要使用handler必须创建looper对象,在主线程中已经默认创建了looper对象,所以在主线程中使用handler不需要创建looper对象。但在子线程就必须自己创建looper对象。创建一个子线程来创建looper对象,获取这个线程的looper对象创建handler,要注意要通过锁机制保证创建handler时looper对象不为空。

# 09 loop方法中的死循环为什么不会卡死?

因为在应用进程启动后,主线程必须一直存在,才不会导致执行完了主线程的代码块就结束了主线程。 在消息列表没有消息的时候,主线程会进去等待、挂起的状态,释放CPU资源,在添加消息的时才会重新唤醒。在Android中会导致卡死的是ANR,并不是主线程中的死循环。

Last Updated: 1/9/2024, 11:22:13 AM