Android Handler消息机制详解8

109 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情

线程中的Looper唯一性:

      1.Looper创建是在prepare()里面触发的,当get()不为null时,会抛异常,确保不会重复创建;当get()为null时,会先通过setInitialValue为对应线程的ThreadLocalMap变量进行赋值,后面通过set()时,会调用ThreadLocalMap的set():key是sThreadLocal,static final修饰,确保key和value唯一;

      2.一个线程对应唯一的ThreadLocalMap变量,当去get()时,会根据当前线程去获取该线程的ThreadLocalMap变量,通过ThreadLocalMap去获取对应的Looper,确保一个线程对应一个Looper。

六.总结

a.Handler机制中最重要的四个对象:

      Handler:负责发送消息及处理消息

      Looper:复制不断的从消息队列中取出消息,并且将消息给发送本条消息的Handler

      MessageQueue:负责存储消息

      Message:消息本身,负责携带数据

b.Looper

      每一个线程中都对应一个Looper[sThreadLocal存储],通过Looper.prepare()为当前线程创建Looper,通过Looper.myLooper()获得当前线程的Looper对象,每一个Looper都对应一个MessageQueue,一个Handler对应唯一Looper,一个Looper可以对应多个Handler;

c.引用关系

      Handler持有Activity的引用,Message持有Handler的引用,MessageQueue持有Message的引用,Looper持有MessageQueue的引用,ActivityThread的main()中创建了sMainLooper;

d.内存泄露问题

      注意Handler使用导致的内存泄漏问题,在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露。当需要在静态内部类中调用外部的Activity时,可以使用弱引用来处理。

七.常见问题

a.一个线程有几个Handler?

      可以有N个Handler;

b.一个线程有几个Looper,如何保证?

      一个线程有1个Looper;

      Looper创建是在prepare()里面触发的,当sThreadLocal.get()不为null时,会抛异常,确保不会重复创建,另外在存储Looper时,用的是对应线程的ThreadLocalMap,然后通过ThreadLocalMap存储时key用的是sThreadLocal,保证key和value唯一;

      一个线程对应唯一的ThreadLocalMap变量,当在sThreadLocal去get()时,会根据当前线程去获取该线程的ThreadLocalMap,通过ThreadLocalMap去获取对应的Looper,确保一个线程对应一个Looper。

c.Handler内存泄露原因?为什么其他内部类没有该问题?

      发送的延迟消息(EmptyMessageDelayed)后、消息处理被前,该消息会一直保存在主线程的消息队列里持续时间,在持续时间里,该消息内部持有对handler的引用,由于handler属于非静态内部类,所以又持有对其外部类(即MainActivity实例)的潜在引用,引用关系如下:

image.png

      这条引用关系会一直保持直到消息得到处理,从而阻止了MainActivity被垃圾回收器(GC)回收,同时造成应用程序的内存泄漏,如下:

image.png

      其他内部类中没有持续持有该内部类应用的东西,则内部类就不会存在一直持有外部类引用的场景,所以不会造成内存泄露。