天天看点

Android检测内存泄漏之leakcanary

内存泄漏,memory leak,开发者经常念叨的一个词,稍不留意,就游走在我们的代码中。Andriod开发,内存泄漏的原因有很多,比如activity的context引用,static引用,广播未取消注册,MVP设计时没有detachView,Rx没有取消subscribe订阅,动画处理等。检测的工具也很多。今天总结下,LeakCanary的使用。看这图,Js接口引用activity泄漏了528kb。

Android检测内存泄漏之leakcanary

一,大话内存泄漏

Java通过垃圾收集器(GC, garbage collection)来自动管理内存。当一个对象不再被使用,就会被自动回收。而“内存泄漏“就是没有成功回收内存的体现。一个对象已经不需要再使用了,但是因为其它的对象持有该对象的引用,导致它的内存不能被回收。“内存泄漏”积累到一定程度,可能导致OOM,所以在写代码的过程中,要注意导致“内存泄漏”的代码写法,提高代码的健壮性。

二,内存泄漏的类型

如果一个对象是可达的有引用的,但实际上它已经没有再使用了,但引用它的对象依然存在,这样的它就是内存泄漏的对象。内存泄漏可分为以下几种类型:

1、静态变量引起的内存泄漏

通常是,一个静态变量,持有对象的应用,对象销毁了,static修饰的变量还在,导致内存无法回收。我之前在BaseActivity中加入实现类到集合中,就造成过内存泄漏。如过静态context一直持有activity的引用,onDestory执行后造成内存泄漏。

public class LeakActivity extends Activity {
    //静态context
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        sContext = this;//赋值后静态context一直持有activity的引用,onDestory执行后造成内存泄漏。
    }
}
           

我遇到的情况,activity当上下文传,js接口引用webview所在的activity,反正不要静态的引用。

2、非静态内部类引起的内存泄漏

如果用android studio开发,写handle发送消息,下面的写法会有黄色警告,因为可能会引发内存泄漏:

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

为什么呢?非静态内部类会引用外部类对象(Activity),当它使用了 postDelayed 的时候,如果 Activity 已经 finish 了,而这个 handler 仍然引用着这个 Activity 就会致使内存泄漏,因为这个 handler 会在一段时间内继续被 mainLooper 持有,导致引用仍然存在,在这段时间内,如果内存不够使,可能OOM了。

3、资源未关闭引起的内存泄漏

IO流的操作,切记要close关闭流,文件读写,网络访问等都要及时关闭字节流、字符流;注册了广播要在onDestory方法中销毁。使用了BraodcastReceiver、Cursor、Bitmap等资源时,需要及时释放掉,若没有释放,则会引起内存泄漏。这个比较好理解。

三,内存泄漏检测工具

用Eclipse开发自带的内存检测工具:Heap。

DDMS中的Heap工具用于大致分析是否存在“内存泄漏”,而MAT工具则用于分析“内存泄漏”发生在哪里,MAT工具中,File->Open Heap Dump,可以打开xxx.hprof文件分析。这个我现在不用。不多说。直接看下面介绍的LeakCanary。

四,LeakCanary,内存泄漏精确检测神器。

要精确地追踪到内存泄漏点,强烈推荐使用Square 公司开源的 LeakCanary开源方案,LeakCanary在Application实现类中一行代码,简单暴力侵入性地捕获内存泄漏代码,甚至捕获Android组件的内存泄漏代码。我发现android动画绘制的时候存在内存泄漏的问题。

1,LeakCanary就像金丝雀监视着你的“煤矿”

不用重复造轮子,直接拿来,android studio开撸步骤:

//在module的build.gradle中:
 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'

//上面是官方推荐写法,其实你要一行也行:
 compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'

//然后在你的Application的子类中install:
public class ExampleApplication extends Application{
  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}
           

Good,好了,LeakCanary这是金丝雀会随着你app的运行,自动安装监测,给你通知。方便你查看那个类出现了内存泄漏,甚至告诉你泄漏了多少M。如图:

Android检测内存泄漏之leakcanary

2,查看log日志,分析LeakCanary收集的数据

一般通过它的通知界面就很明了了,如果要详细看看过程,看log部分如下:SplashActivity has leaked!

Android检测内存泄漏之leakcanary

五,总结

遇到内存泄漏不是什么好事,所以平时写代码,留个心眼,以预防为主。一些通用的解决方案:

1,使用Application的context

需要上下文的时候,如果不是非得activity对象,传入Application的context,因为Application的context的生命周期比Activity长,它是app全局的,相当于static的生命周期。

2,static变量不要引用view实例

3,关闭资源,close,unsubscribe,unregister,null记得吃药。

欢迎交流,Dusan,杜乾,291902259!OpenDeveloper!