防内存泄露(一)内部类方内存泄露

本文探讨了内部类导致的内存泄露问题,特别是HandlerLeak的解决方案。通过将继承Handler的内部类声明为静态并使用WeakReference持有外部类引用,可以避免内存泄露。同时,文章提醒注意匿名内部类如线程、Handler在内存管理中的风险,强调了volatile关键字的作用以及静态变量可能导致的问题。最后,举例说明了如何避免由ImageView和静态背景造成的内存泄露。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HandlerLeak的正确改法:

把继承handler的内部类声明为static,然后在实例化时把外部类引用传进去,用WeakReference拿着,使用时判空。


实例:

public class GiftAnimationController {
    /*
     * 次要信息省略
     */

    static class MyHandler extends SafeDispatchHandler{
        WeakReference<GiftAnimationController> wc;

        public MyHandler(GiftAnimationController gc) {
            this.wc =new WeakReference<GiftAnimationController>(gc);
        }
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == SLIDE_IN_WHAT) {
                //MLog.verbose(this, "animatingViews.size() = " + animatingViews.size());
                if(wc==null){
                    return;
                }
            } else if (msg.what == SLIDE_OUT_WHAT) {
            }
        }
    }
    private Handler handler = new MyHandler(this);
}


另外注意匿名内部类的内存泄露问题(转):

首先先看一下下面这样一段代码

@Override
protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      new Thread(new Runnable() {
        @Override
public void run() {
while (true) {
SystemClock.sleep(1000);
}
}
}).start();
}

Thread 是一个匿名内部类,,当我们finish的时候,该activity实例不会真正销毁,GC机制也不会进行该实例的垃圾回收,因为匿名内部类和非静态内部类持有外部类的强引用,也就是说Thread持有外部activity的强引用,而thread内部while(true)是死循环,线程不会停止,对外部activity的强引用也不会消失。这样就造成了 memory leak,内存溢出。通常情况下我们可以设置一个flag,在activity,的生命周期ondestroy中改变flag的状态,但是!!!主线程和子线程的执行时有线程调度的,也就是说会造成竞争的现象,两个线程会争夺cup时间片的执行权,额。。越挖越深,这又会造成我们虽然改变的flag的状态,但是,子线程中的flag并不是马上就能改变值,因为jvm memory model,原型和执行原理,所有的线程都是在自己的工作内存中工作的,对值得操作是先从主内存读取到工作线程的工作内存中,然后cpu对工作内存的值进行修改,最后再写回主内存,所以主线程对flag的操作可能恰好的发生在子线程已经读完主内存的值到工作内存,但是还没有执行的这段时间,所以,我们应该让flag的状态改变能够让子线程马上可见,应该在声明flag的时候加上volatile关键字,虽然该关键字不能保证操作的原子性,但是能够保证变量flag的可见性。当然,还有,我们可以声明一个静态类。但是!!!慎用静态类与静态变量,比如当我们旋转屏幕,可能会造成activity的销毁与重建(虽然国内很多应用不允许旋转屏幕),不希望重新加载图片,所以,有些人会这样写:

    private static Drawable background;然后,这样:ImageView imageView = new ImageView(MainActivity.this);

imageView.setBackground(background);很聪明,这样无论旋转多少次屏幕,bitmap都不会重新加载了,省去了大量的时间,但是!!!imageView 引用了MainActivity的context。imageView.setBackground(background);这句源码中(父类View中)background.setCallback(this);background又引用了imageView的回调强引用,这样旋转屏幕,就会造成之前的MainActivity不能被销毁和回收,他的生命甚至和static一样长,所以,内存泄露了。记得Android官方博有个经典的例子和这个很像。当然,还有很多情况会造成内存泄露,譬如,我们经常这样声明一个匿名内部handler:

  privateHandler handler = newHandler(){
  @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};

和匿名内部线程thread一样,会造成内部泄露的隐患。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值