Handler机制
应用启动是从 ActivityThread 的 main 开始的,先是执行了 Looper.prepare(),该方法先是 new 了一个 Looper 对象,在私有的构造方法中又创建了 MessageQueue 作为此 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定 MainThread 中;
当我们创建 Handler 子类对象时,在构造方法中通过 ThreadLocal 获取绑定的 Looper 对象,并获取此 Looper 对象的成员变量 MessageQueue 作为该 Handler 对象的成员变量;
在子线程中调用上一步创建的 Handler 子类对象的 sendMesage(msg) 方法时,在该方法中将 msg 的 target 属性设置为自己本身,同时调用成员变量 MessageQueue 对象的 enqueueMessag() 方法将 msg 放入 MessageQueue 中;
主线程创建好之后,会执行 Looper.loop() 方法,该方法中获取与线程绑定的 Looper 对象,继而获取该 Looper 对象的成员变量 MessageQueue 对象,并开启一个会阻塞(不占用资源)的死循环,只要 MessageQueue 中有 msg,就会获取该 msg,并执行 msg.target.dispatchMessage(msg) 方法(msg.target 即上一步引用的 handler 对象),此方法中调用了我们第二步创建 handler 子类对象时覆写的 handleMessage() 方法。
Looper.loop()为什么不会阻塞主线程
卡死是指不响应消息,而那个循环就是一个消息处理循环。 Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程
使用Handler需要注意什么问题,怎么解决的?
Handler类由于无设置为静态类,从而导致了内存泄漏,最终的内存泄漏发生在持有Handler类的外部类 原因
a. 主线程的Looper对象的生命周期 = 该应用程序的生命周期
b. 在Java中,非静态内部类&匿名内部类都默认持有外部类的引用 所以说,在Handler消息队列中还有未处理的消息/正在处理的消息时,消息队列中的Message持有Handler实例的引用。
由于Handler =非静态内部类/匿名内部类(2种使用方式),故又默认持有外部类的引用(MainActivity实例),上述引用关系会一直保持,直到Handler消息队列中的所有消息被处理完毕。
在Handler消息队列还有未处理的消息/正在处理消息时,此时若需销毁外部类MainActivity,但由于上述的引用关系,垃圾回收器(GC)无法回收MainActivity,从而造成内存泄漏。
解决方案 静态内部类+弱引用
// 定义弱引用实例
private WeakReference<Activity> reference;
// 在构造方法中传入需要持有的Activity实例
public MyHandler (Activity activity){
reference = new WeakReference<Activity>(activity);
}
当外部类结束生命周期时,清空Handler内的消息队列
mHandler.removeCallbacksAndMessage(null);