这就需要从消息机制的原理说起。因为在之前的博客,或者网上有很多介绍这部分原理,所以这里就直接进入主题。
我们知道在消息机制中的流程为handler最终通过enqueueMessage将一个Message添加到消息队列MessageQueue中,然后每个线程的Looper会循环的判断是否有消息,如果存在就从消息队列中取出消息。由于每个消息对象中都关联了发送消息的Handler实例对象target,那么在消息队列中取出Message的对象的时候,可以通过target获取到Handler对象,最终通过Handler的dispatchMessage来进行消息的发送。因为Handler实例化在主线程中,所以这里可以完成子线程和主线程的一次数据传递。
那么,这个过程在什么地方会导致内存泄漏呢?
这里直接给出答案,因为如果消息Message通过when字段来进行比较,最终添加到一个有序的链表MessageQueue中。当我们的Activity退出了,但是在消息队列中仍然有当前Activity中的Handler未处理完毕的消息的时候,就会导致内存泄漏!这是为什么呢?不妨看看我们通常非静态Handler是怎么写的:
Handler handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
因为在Handler中handleMessage方法是一个空实现,比如:
public void handleMessage(@NonNull Message msg) {
}
而我们在处理消息的时候,就需要自己去实现这个方法。故而使用前面的匿名内部类方式来创建一个Handler的示例。
我们知道,匿名内部类会隐式的持有外部类的示例对象,故而在这个Handler的示例对象中,其实会持有已经销毁的Activity的示例,而在消息队列中还有待处理的消息的时候,那么这个Handler也就不会被回收。那么将导致这个本该被销毁的Activity不会被回收。也就是内存泄漏。
我们通过上面已经知道了原因,那么对应的可以有如下的几种解决方法。
由于静态的Handler的生命周期为应用的生命周期,那么当我们使用静态的Handler的时候,其实并不需要为每个Activity都创建一个静态的Handler,因为这样就不太合理了。在Java语言中,默认创建的实例为强引用对象,也就是说如果不置为null,那么这个对象就永远不会被回收,除非程序终止。
那么,毫无疑问这样将导致很多空间的浪费。且在Message中提供了很多字段来标识消息的类别,故而应该将Handler复用,而不是创建多个。且创建过程需要分配空间,其实会消耗一定的效率。
所以,处于效率和空间的考虑,在使用静态Handler的时候,还是尝试使用单例模式来做。比如下面的案例:
public class MyHandler {
// 锁对象
private static final Object mObject = new Object();
private static Handler mHandler;
private MyHandler(){}
public static Handler getHandler(IHandler iHandler){
if(mHandler == null){
synchronized (mObject){
if(mHandler == null){
mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
iHandler.handleMessage(msg);
}
};
}
}
}
return mHandler;
}
// 定义一个回调接口
public interface IHandler{
void handleMessage(@NonNull Message msg);
}
}
然后再使用的时候,比如:
public class ThreeActivity extends AppCompatActivity implements MyHandler.IHandler {
private ProgressBar progressbar;
private Handler handler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three);
progressbar = findViewById(R.id.progressbar);
progressbar.setMax(100);
handler = MyHandler.getHandler(this);
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.obj = 50;
msg.what = 1;
handler.sendMessage(msg);
}
}).start();
}
@Override
public void handleMessage(@NonNull Message msg) {
if(msg.what == 1){
progressbar.setProgress((int) msg.obj);
}
}
}
测试结果:
前面我们知道当消息队列中还有未处理的消息的时候,才会导致Activity的内存泄漏,故而可以在这个Activity退出的时候,清空当前的消息队列可以达到预防的目的。
即:在每个Activity的onDestroy生命周期方法中调用removeCallbacksAndMessages函数,比如:
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
前面提到过使用静态Handler的各种好处,这里继续使用静态Handler来实现,只是在其中使用弱引用。
private static class StaticHandler extends Handler{
private WeakReference<Activity> mWeakReference;
public StaticHandler(Activity activity){
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
Activity activity = mWeakReference.get();
if(activity == null) return;
// todo 处理消息
}
}
这个代码,参考自博客:Android使用Handler造成内存泄露的分析及解决方法。
有种豁然开朗的感觉,因为在handleMessage中判断一次,就可以做到避免。且本身来说定义为static,那么不存在匿名内部类隐式持有问题,而且在这个静态类中,将传入的Activity实例对象使用弱引用来修饰,那么可以在GC的时候被回收,然后及时的在handleMessag时候判断一次,更加安全可靠。
/**
* Date: 2021年9月28日 10:11:52
* Author: 梦否
* Example:
* MyHandler myHandler = MyHandler.getHandler(this);
* myHandler.setActivityReference(this);
* handler = myHandler.getHandler();
*/
public class MyHandler {
// 锁对象
private static final Object mObject = new Object();
private Handler mHandler;
private WeakReference<Activity> mWeakReference;
private static MyHandler mMyHandler;
private MyHandler(IHandler iHandler){
mHandler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
Activity activity = mWeakReference.get();
if(activity != null){
iHandler.handleMessage(msg);
}
}
};
}
/**
* 虚引用,为了完成逻辑:当前activity不存在时候不处理
* @param activity
*/
public void setActivityReference(Activity activity){
mWeakReference = new WeakReference<>(activity);
}
public Handler getHandler(){
return this.mHandler;
}
/**
* 单例,为了复用
* @param iHandler
* @return
*/
public static MyHandler getHandler(IHandler iHandler){
if(mMyHandler == null){
synchronized (mObject){
if(mMyHandler == null){
mMyHandler = new MyHandler(iHandler);
}
}
}
return mMyHandler;
}
public interface IHandler{
void handleMessage(@NonNull Message msg);
}
}
Thanks