前提场景:Activity中new一个Handler并重写handleMessage方法
public class SingleTopActivity extends Activity {
@SuppressLint("HandlerLeak") Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); LogUtil.i("name ====== " + SingleTopActivity.this.getClass().getSimpleName()); } };
...
}
疑问点1、在Activity finish的时候执行handler = null 是否真正销毁了Handler?
答:并不会,这个取决于通过handler发送的消息是否都处理完成,因为message 的target字段就是指向了当前的Handler,就是说每一个message都持有了一份当前Handler的引用,所以执行handler = null,只是当前的引用变成了 null,在message中可能还有其他的应用,因此new Handler时 在堆中开辟的内存不会被系统回收。
验证现象:执行handler = null以后,之前发送的延时任务仍然会被执行,handleMessage方法仍然会被正常回调。
疑问点2、在前提场景中,handler默认持有了Activity的应用,有内存泄漏的风险,那在关闭页面时,主动执行Activity的 onDestroy方法能解决可能出现的内存泄漏问题吗?
答:不能解决问题,其实没有必要主动执行onDestroy方法 因为你执行了finish后 过一会系统就会自动执行onDestroy ,此时通过命令adb shell dumpsys activity | grep com.xxx.xxx 查看Activity栈信息,发现activity也已经从栈中移除掉了,但是如果此时有未执行完的消息(引用关系:activity -- handler -- message),activity在堆中的”真身”还是存在的,并不会被系统回收。(引用位置在handler所在的栈的位置)
另:查看栈信息截图 图中不存在SingleTopActivity了
验证现象:执行handler = null以后,之前发送的延时任务仍然会被执行,LogUtil.i("name ====== " + SingleTopActivity.this.getClass().getSimpleName()); 打印信息正常显示,并不会报空指针。
验证现象2:当有未执行完成的message时,SingleTopActivity中的finalize方法没有被执行,message执行完后,过了一会执行了finalize方法(GC在回收一个对象前会执行此对象的finalize方法)
验证现象3:通过AS自带的Memory分析工程查看内存,发现内存一直在增长,SingleTopActivity在增加
从内存分析图的右下角 Allocation Call Stack 也能看到SingleTopActivity在栈中的被引用的位置 就在Handler里面
通过以上分析验证,我们就明白了为什么在Activity关闭时把handler置为null,然后主动执行onDestroy方法,并不能解决可能发生的内存泄漏问题。
推荐最佳解决方案:在Activity关闭时执行 handler.removeCallbacksAndMessages(null); (静态+弱应用的方案 第一增加static对象,第二弱应用可能被回收影响业务,不推荐)