一起养成写作习惯!这是我参与「掘金日新计划 · 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实例)的潜在引用,引用关系如下:
这条引用关系会一直保持直到消息得到处理,从而阻止了MainActivity被垃圾回收器(GC)回收,同时造成应用程序的内存泄漏,如下:
其他内部类中没有持续持有该内部类应用的东西,则内部类就不会存在一直持有外部类引用的场景,所以不会造成内存泄露。