Handler
Handler的原理
Handler的原理:Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI的。所以就有了Handler,它的作用就是实现线程之间的通信。
Handler整个流程中,主要有四个对象,handler,Message,MessageQueue,Looper。当应用创建的时候,就会在主线程中创建handler对象,我们通过要传送的消息保存到Message中,handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop()方法 不断的从MessageQueue中取出Message交给handler进行处理。从而实现线程之间的通信。
线程间通信的实现步骤
线程间通信的实现步骤:
- 在主线程中定义Handler的子类
- 重写Handler类的handleMessage()方法
- 用该子类定义全局的Handler对象,以便子线程使用
- 子线程获得handler对象用该对象的sendMessage()方法发送消息
Handler在多线程中的应用
- 发送消息,在不同的线程间发送消息,使用的方法为sendXXX();android.os.Handler对象通过下面的方法发送消息的:
sendEmptyMessage(int):发送一个空的消息;
sendMessage(Message):发送消息,消息中可以携带参数;
sendMessageAtTime(Message, long):未来某一时间点发送消息;
sendMessageDelayed(Message, long):延时Nms发送消息。
计划任务,在未来执行某任务,使用的方法为postXXX();。
android.os.Handler对象通过下面的方法执行计划任务:
post(Runnable):提交计划任务马上执行;
postAtTime(Runnable, long):提交计划任务在未来的时间点执行;
postDelayed(Runnable, long):提交计划任务延时Nms执行。
如何在子线程中创建Handler
如何在子线程中创建Handler?
创建一个 HandlerThread,即创建一个包含 Looper 的线程HandlerThread 的构造函数有两个
通过 HandlerThread 的 getLooper 方法可以获取 Looper
通过 Looper 我们就可以创建子线程的 handler 了
通过该 handler 发送消息,就会在子线程执行;
如果要 handlerThread 停止: handlerThread.quit();
Hander中removeMessages方法
Hander中removeMessages方法
这个方法使用的前提是之前调用过sendEmptyMessageDelayed(0, time),意思是延迟time执行handler中msg.what=0的方法;
在延迟时间未到的前提下,执行removeMessages(0),则上面的handler中msg.what=0的方法取消执行;
在延迟时间已到,handler中msg.what=0的方法已执行,再执行removeMessages(0),不起作用。
该方法会将handler对应message队列里的消息清空,通过msg.what来找到对应的message。
当队列中没有message则handler会不工作,但并不是handler会停止,当队列中有新的message进来后,会继续处理执行。
Handler内存泄漏
Handler发生内存泄漏的情况
Handler发生内存泄漏的情况:非静态内部类,或者匿名内部类。
使得Handler默认持有外部类的引用。在Activity销毁时,由于Handler可能有未执行完/正在执行的Message。导致Handler持有Activity的引用。进而导致GC无法回收Activity
发送延迟消息
第一种情况,是通过handler发送延迟消息,我们在HandlerActivity中,发送一个延迟20s的消息。然后打开HandlerActivity后,马上finish。看看会不会内存泄漏。
我们的HandlerActivity发生了内存泄漏,从引用路径来看,是被匿名内部类的实例mHandler持有引用了,而Handler的引用是被Message持有了,Message引用是被MessageQueue持有了…
结合我们所学的Handler知识和这次引用路径分析,这次内存泄漏完整的引用链应该是:
主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity
所以这次引用的头头就是主线程,主线程肯定是不会被回收的,只要是运行中的线程都不会被JVM回收,跟静态变量一样被JVM特殊照顾。
子线程运行没结束
第二个实例,是我们常用到的,在子线程中工作,比如请求网络,然后请求成功后通过Handler进行UI更新运行中的子线程 —> Activity
当然,这里的Handler也是持有了Activity的引用的,但主要引起内存泄漏的原因还是在于子线程本身,就算子线程中不用Handler,而是调用Activity的其他变量或者方法还是会发生内存泄漏。
所以这种情况我觉得不能看作Handler引起内存泄漏的情况,其根本原因是因为子线程引起的,如果解决了子线程的内存泄漏,比如在Activity销毁的时候停止子线程,那么Activity就能正常被回收,那么也不存在Handler的问题了
解决内存泄漏
解决内存泄漏
不要让长生命周期对象持有短生命周期对象的引用,而是用长生命周期对象持有长生命周期对象的引用
比如Glide使用的时候传的上下文不要用Activity而改用Application的上下文(这句有问题,并无此说法,在此修正)。还有单例模式不要传入Activity上下文
将对象的强引用改成弱引用
强引用就是对象被强引用后,无论如何都不会被回收。 弱引用就是在垃圾回收时,如果这个对象只被弱引用关联(没有任何强引用关联他),那么这个对象就会被回收。 所以我们将对象改成弱引用,就能保证在垃圾回收时被正常回收。软引用就是在系统将发生内存溢出的时候,回进行回收。 虚引用是对象完全不会对其生存时间构成影响,也无法通过虚引用来获取对象实例,用的比较少
内部类写成静态类或者外部类
跟上面Hanlder情况一样,有时候内部类被不正当使用,容易发生内存泄漏,解决办法就是写成外部类或者静态内部类
在短周期结束的时候将可能发生内存泄漏的地方移除
比如Handler延迟消息,资源没关闭,集合没清理等等引起的内存泄漏,只要在Activity关闭的时候进行消除即可
Android之Handler、Message、MessageQueue、Looper详解2:https://developer.aliyun.com/article/1473573