重谈Handler的内存泄漏

Handler 的内存泄漏问题

在多线程操作中,handler会使用的非常多,但是每次使用handler你有没有考虑内存泄漏的问题。

如果你使用handler进行操作时,你会发现出现以下提示 This Handler class should be static or leaks might occur (anonymous android.os.Handler)这样的提示。翻译: 由于此Handler被声明为内部类,因此可能会阻止外部类被垃圾回收。 如果Handler使用Looper或MessageQueue作为主线程以外的线程,则没有问题。 如果Handler正在使用主线程的Looper或MessageQueue,则需要修复Handler声明,如下所示:将Handler声明为静态类; 在外部类中,实例化外部类的WeakReference,并在实例化Handler时将此对象传递给Handler; 使用WeakReference对象对外部类的成员进行所有引用。

警告原因:handler没有设置为静态类,声明内部类可能会阻止被GC回收,从而导致内存泄漏

那么为什么会造成内存泄漏呢。 首先来说下什么是内存泄漏 内存泄漏(Memory Leak):指的是程序已经动态分配的堆内存由于某种原因程序未释放或者无法释放,造成系统资源浪费,会造成程序运行缓慢甚至系统崩溃等严重后果。 问题代码:

public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler();
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv);
        //模拟内存泄漏
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mTextView.setText("yiyi");
            }
        }, 1000);
    }
}
复制代码

内存泄漏原因

从上面问题代码,可以看出这里通过内部类方式创建handler,而在java中,非静态内部类会持有外部类的引用,这里的postDelayed是一个延迟处理消息,将一个handler装入到message中,将消息放进消息队列messageQueueLooper进行取消息进行处理。如果此时activity要退出了,想要调用**destroy**销毁,但是此时Looper正在处理消息,**Looper**的生命周期明显比activity长,这将使得activity无法被**GC**回收,最终造成内存泄漏。并且此时handler还持有activity的引用,也是造成内存泄漏的一个原因(不是根本原因)。

但是我觉得真正handler造成内存泄漏的根本原因是生命周期比activity长,比如TextView也是内部类创建的,那么它怎么没有造成内存泄漏,它也持有外部类Activity的引用,根本原因是它的生命周期比Activity短,Activity销毁时候,它可以被GC回收

总结

当handler有没有处理的消息或者正在处理消息,此时Handler的生命周期明显比Activity长,GC持有Activity与handler两者的引用,导致Activity无法被GC回收,造成内存泄漏。而handler是不是内部类,并不是造成内存泄漏的根本原因。

解决方案

静态内部类+弱引用

将Handler的子类设置成 静态内部类,并且可加上 使用WeakReference弱引用持有Activity实例

原因:弱引用的对象拥有短暂的生命周期。而垃圾回收器不管内存是否充足都会回收弱引用对象。

public class HandlerActivity extends AppCompatActivity  {
    private static class MyHandler extends Handler {
    private final WeakReference<HandlerActivity> mActivity;
    public MyHandler(HandlerActivity activity) {
        mActivity = new WeakReference<HandlerActivity>(activity);
    }
 
    @Override
    public void handleMessage(Message msg) {
        HandlerActivity activity = mActivity.get();
        if (activity != null) {
        }
    }
 
    private final MyHandler mHandler = new MyHandler(this);
    private static final Runnable mRunnable = new Runnable() {
        @Override
        public void run() { }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler.postDelayed(mRunnable, 1000 * 60 * 1);
        finish();
    }
}
复制代码

Activity生命周期结束时,清空消息队列 只需在Activity的onDestroy()方法中调用mHandler.removeCallbacksAndMessages(null);就行了。

@Override
protected void onDestroy() {
    super.onDestroy();
    if(handler!=null){
        handler.removeCallbacksAndMessages(null);
        handler = null;
    }
}
复制代码

Guess you like

Origin juejin.im/post/7068643522735243278