一、LeakCanary简介
LeakCanary是Square公司为Android开发者提供的一个自动检测内存泄漏的工具,LeakCanary本质上是一个基于MAT进行Android应用程序内存泄漏自动化检测的的开源工具,我们可以通过集成LeakCanary提供的jar包到自己的工程中,一旦检测到内存泄漏,LeakCanary就会dump Memory信息,并通过另一个进程分析内存泄漏的信息并展示出来,随时发现和定位内存泄漏问题,而不用每次在开发流程中都抽出专人来进行内存泄漏问题检测,*大地方便了Android应用程序的开发。

二、LeakCanary工作机制
RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把 heap 内存 dump 到 APP对应的文件系统中的一个 .hprof 文件中。
在另外一个进程中的 HeapAnalyzerService 有一个HeapAnalyzer 使用HAHA 解析这个文件。
得益于唯一的 reference key, HeapAnalyzer 找到KeyedWeakReference,定位内存泄露。
HeapAnalyzer 计算到 GC roots的*短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
引用链传递到 APP 进程中的DisplayLeakService, 并以通知的形式展示出来。
三、LeakCanary的Android Studio集成
1. 在build.gradle中添加LeakCanary的依赖包
debugImplementation ‘com.squareup.leakcanary:leakcanary-android:1.6.3’
releaseImplementation ‘com.squareup.leakcanary:leakcanary-android-no-op:1.6.3’

注意: debug和release版本要一致,否则会报错。

2. 在我们自定义Application的onCreate方法中注册LeakCanary
public class LeakApplication extends Application {
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) { //1
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
}
}

注释1处的代码用来进行过滤操作,如果当前的进程是用来给LeakCanary 进行堆分析的则return,否则会执行LeakCanary的install方法。这样我们就可以使用LeakCanary了,如果检测到某个Activity 有内存泄露,LeakCanary 就会给出提示。

3. 重写Application
上述代码只能够检测Activity的内存泄漏,当然还存在其他类的内存泄漏,这时我们就需要使用RefWatcher来进行监控。重写Application,如下所示:

public class LeakApplication extends Application {
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher= setupLeakCanary();
}
private RefWatcher setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
}
return LeakCanary.install(this);
}
public static RefWatcher getRefWatcher(Context context) {
LeakApplication leakApplication = (LeakApplication) context.getApplicationContext();
return leakApplication.refWatcher;
}
}

install方法会返回RefWatcher用来监控对象,LeakApplication中还要提供getRefWatcher静态方法来返回全局RefWatcher。
注意: 需要在AndroidManifest.xml文件中添加android:name=”.LeakApplication”,指定Application子类,当应用启动时,这个类的实例被*个创建。这个属性是可选的,大多数APP都不需要这个属性。在没有这个属性的时候,Android会启动一个Application类的实例。

%title插图%num
4. 在activity或fragment中使用leak canary举例
public class SearchActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LeakThread leakThread = new LeakThread();
leakThread.start();
}
class LeakThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(6 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = LeakApplication.getRefWatcher(this);//1
refWatcher.watch(this);
}
}

SearchActivity存在内存泄漏,原因就是非静态内部类LeakThread持有外部类SearchActivity的引用,LeakThread中做了耗时操作,导致SearchActivity无法被释放。
在注释1处得到RefWatcher,并调用它的watch方法,watch方法的参数就是要监控的对象。当然,在这个例子中onDestroy方法是多余的,因为LeakCanary在调用install方法时会启动一个ActivityRefWatcher类,它用于自动监控Activity执行onDestroy方法之后是否发生内存泄露。这里只是为了方便举例,如果想要监控Fragment,在Fragment中添加如上的onDestroy方法是有用的。

public abstract class BaseFragment extends Fragment {
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = App.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}

运行程序,这时会在界面生成一个名为Leaks的应用图标。接下来不断的切换横竖屏,这时会闪出一个提示框,提示内容为:“Dumping memory, app will freeze.Brrrr.”。再稍等片刻,内存泄漏信息就会通过Notification展示出来,比如荣耀magic的通知栏如下图1所示。
Notification中提示了SearchActivity发生了内存泄漏。点击Notification就可以进入内存泄漏详细页,除此之外也可以通过Leaks应用的列表界面进入,列表界面如下图2所示。内存泄漏详细页如下图3所示。
点击加号就可以查看具体类所在的包名称。整个详情就是一个引用链:SearchActivity的内部类LeakThread引用了LeakThread的this$0,this$0的含义就是内部类自动保留的一个指向所在外部类的引用,而这个外部类就是详情*后一行所给出的SearchActivity的实例,这将会导致SearchActivity无法被GC(garbage collection,垃圾回收),从而产生内存泄漏。
图1:%title插图%num

图2:%title插图%num

图3:%title插图%num

解决该内存泄露的方法就是将LeakThread改为静态内部类。再次运行程序LeakThread就不会给出内存泄漏的提示了。


static class LeakThread extends Thread {

}

四、LeakCanary2使用
1. 和 LeakCanary1 相比,LeakCanary2 有以下改动
完全使用 Kotlin 重写。
使用新的Heap分析工具Shark,替换到之前的haha,按官方的说法,内存占用减少了10倍。
泄露类型分组。
2. LeakCanary2集成
只需要增加以下依赖即可:

debugImplementation ‘com.squareup.leakcanary:leakcanary-android:2.2’
1
LeakCanary2 实现了自动调用 install() 方法,实现方式是使用的 ContentProvider,相关代码位于 leakcanary-object-watcher-android 模块中的 AppWatcherInstaller.kt 中。
AppWatcherInstaller 继承 ContentProvider,重写了 onCreate() 方法,这里利用的是,注册在 Manifest 文件中的 ContentProvider,会在应用启动时,由 ActivityThread 创建并初始化。
————————————————