Android常见内存泄露和解决办法

2,211 阅读2分钟

内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。用完没有释放,造成可用内存越来越少。这里就不得不提一下Java GC机制Java四种引用方式

在实际项目中稍有不慎就有可能导致内存泄露,下面梳理一下常见的可能造成内存泄露的代码和解决办法

Handler

在Android的多线程开发中,估计永远无法绕开handler了(不知道kotlin的协程是怎么实现的?),但是使用Handler却很容易出现问题,看下面代码:

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

Message message = new Message();
handler.sendMessageDelayed(message,2000);

不幸的告诉你,这段代码已经造成了内存泄露。 首先需要知道的是:非静态内部类默认持有外部类的引用message send出去的时候,会进入MessageQueue中,Looper会不停的从MessageQueue中取出Message并分发执行。如果message没有被执行完毕,handler就不会被释放,如果此时activity被销毁了,由于Handler持有当前Activity的引用,Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放。jvm无法对activity执行GC,Activity就泄漏。

解决办法:

  1. 使用弱引用(Java四种引用方式
public static class MyHandler extends Handler{
    WeakReference<ResolveLeakcanaryActivity> reference;

    public MyHandler(WeakReference<ResolveLeakcanaryActivity> activity){
      reference = activity;
    }

    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if (reference.get()!=null){
        //注意必须要要进行判空
      }
    }
  }
  1. 调用Handler.removeMessages() 在onDestory中可以将handler中的message都移除掉,没有延时任务要处理,activity的生命周期就不会被延长,则可以正常销毁。

单例

public class Initializer {

  private Initializer instance;

  private Context context;

  private MyHelper(Context context){
    this.context = context;
  }

  public static synchronized Initializer getInstance(Context context){
    if (instance == null){
      instance = new Initializer(context);
    }
    return instance;
  }

}

当需要Context对象进行初始化时,如果错误的使用:

Initializer.getInstance(this)

就会存在内存泄露的风险,因为单例中的static实例持有Activity,但是static变量的生命周期是整个应用的生命周期,当Activity finish时,activity实例被static变量持有不能释放内存,导致内存泄漏。

解决办法:

  1. 使用getApplicationContext() 在Application里初始化,或者使用getApplicationContext()作为Context

匿名内部类

泄露原因和Handler一样,解决方案也类似,静态内部类+弱引用方式调用就行了。

RxJava和网络请求

如果网络请求数据返回缓慢,Activity在请求成功之前就被结束,但是这时网络请求还未结束,回调接口为内部类依然会持有Activity的对象,这时Activity就内存泄漏的。 解决办法就是接触回调监听,可以使用lifecycle管理生命周期或者直接使用jetpack中的LiveData组件。