- 关于Handler内存泄露可以看下面的博客,我经常看的博主
Android 内存泄露:详解 Handler 内存泄露的原因
Handler的内存泄露就是Handler默认持有了Activity或这Fragment的引用,从而导致Activity销毁后,Handler还持有Activity的引用导致Activity不能被gc回收。解决方案就是静态内部类+弱引用,使用方式可以学Fragment在Activity的绑定与解绑方式,因为静态类不会持有外部类的 引用。
我用的kotlin,Java的方式代码也是一样,自己去写了,懒得贴了
/**
* Handler,避免内存泄露
*/
private class CustomHandler : Handler() {
private lateinit var weakReference: WeakReference<HomeFragment>
fun attachHandler(homeFragment: HomeFragment) {
weakReference = WeakReference(homeFragment)
}
fun detachHandler() {
weakReference.clear()
}
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
when (msg?.what) {
100 -> {
// 然后这里使用weakReference.get()获取弱引用保存的对象
// 我这里是HomeFragment
}
}
}
}
Handler的三种用法
模拟子线程发送数据
new Thread(new Runnable() {
@Override
public void run() {
// 休眠5秒
SystemClock.sleep(5000);
Message message = Message.obtain();
message.obj = "Handler赋值";
//handler1.sendMessage(message);
// 如果在休眠5秒的时间内退出界面(界面销毁),
// 但是当休眠5秒结束此处仍然会发送消息
// 就会造成内存泄漏
if (handler2 != null) handler2.sendMessage(message);
handler3.post(new Runnable() {
@Override
public void run() {
tvHandler.setText("Handler赋值");
}
});
}
}).start();
1、第一种用法
/**
* 第一种用法
*/
private Handler handler1 = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
tvHandler.setText(msg.obj.toString());
}
};
2、第二种用法,推荐使用第二种用法
/**
* 第二种用法,推荐使用第二种用法
*/
private Handler handler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
tvHandler.setText(msg.obj.toString());
return false;
}
});
为什么推荐第二种用法,我们可以查看Handler源码的消息分发
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
// 1、此处是Handler的post用法,第三种用法
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2、此处就是我们第二种用法传入的callback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3、此处就是第一种用法,算是Handler消息分发最后的倔强,哈哈
// 用来保底接收数据
handleMessage(msg);
}
}
3、Handler第三种用法,Post方法,注释1处执行的就是post传递的Runnable对象
private Handler handler3 = new Handler();
// 直接在子线程中使用post方法刷新UI
handler3.post(new Runnable() {
@Override
public void run() {
tvHandler.setText("Handler赋值");
}
});
查看post方法的源码,我们看到Handler源码中将Runnable对象
封装到了Message对象中。
public final boolean post(@NonNull Runnable r) {
// post方法还是调用的sendMessageDelayed,最终
// 还是会调用到sendMessageAtTime
return sendMessageDelayed(getPostMessage(r), 0);
}
// 通过如下代码,我们看到将Runnable对象封装到了Message中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
// 将Runnable对象封装到了Message的callback中
m.callback = r;
return m;
}
最后我们回到dispatchMessage方法
public void dispatchMessage(@NonNull Message msg) {
// 从上述源码分析,我们知道此处就是post方法的使用
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
最后看到handleCallback方法,其实就是调用了Runnable的run方法
执行run方法中的代码
private static void handleCallback(Message message) {
message.callback.run();
}
Handler的内存泄漏处理
1、弱引用处理(代码在最上方)
2、删除消息和回调callback
这种方式需要有消息在消息队列并且使用第二种方式使用Handler,
否则这种处理内存泄漏的方式应该还是会造成内存泄漏
@Override
public void onDestroy() {
super.onDestroy();
// 清除消息和回调的callback
handler2.removeCallbacksAndMessages(null);
}
我验证过如下方式,在5秒消息等待之前销毁Activity,使用第一种方式创建爱女Handler还是会发送消息,还是会造成内存泄漏
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(5000);
Message message = Message.obtain();
message.obj = "Handler赋值";
message.what = 100;
if (handler2 != null) handler1.sendMessage(message);
}
}).start();
即使调用了removeCallbacksAndMessages方法,第一种方式创建的
Handler仍然能收到消息,造成内存泄漏
private Handler handler1 = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
LogUtils.d(TAG, "我还是收到消息了");
tvHandler.setText(msg.obj.toString());
}
};
3、Handler置空
将Handler置空之后,也就不会再继续发送消息了
@Override
public void onDestroy() {
super.onDestroy();
// 为了保险可以清除一次数据
handler2.removeMessages(100);
// 将Handler置空,这个跟第一种弱引用有异曲同工
handler2 = null;
}
我们解释一下为啥清除数据是可选的,比如我们模拟消息发送的例子,
1、休眠5秒之后在发送消息,当在休眠5秒的时间段内退出界面,此时消息
还没有存储到消息队列,所以此时清除消息是没有消息可清除的。
就像如下代码
SystemClock.sleep(5000);
Message message = Message.obtain();
message.obj = "Handler赋值";
message.what = 100;
if (handler2 != null) handler2.sendMessage(message);
2、比如如下代码,清除数据是会生效的,因为此时已经将消息发送到消息队列中。
Message message = Message.obtain();
message.obj = "Handler赋值";
message.what = 100;
// 使用Handler提供的延迟消息发送,此时消息已经存到消息队列
if (handler2 != null) handler2.sendMessageDelayed(message, 5000);
因此,推荐使用第一种和第三种处理内存泄漏的方式