内存泄漏是困扰开发者的一大问题,上篇讲述了如何用MAT分析hprof文件来优化内存,那么今天来介绍一下Leakcanary这个更智能的好东东:
Leakcanary的原理
-
RefWatcher.watch()创建一个 KeyedWeakReference 到要被监控的对象。 -
然后在
AndroidWatchExecutor的后台线程检查引用是否被清除,如果没有,调用GC。 -
如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个
.hprof文件中。 -
在另外一个进程中的
HeapAnalyzerService有一个HeapAnalyzer使用HAHA 解析这个文件。 -
得益于唯一的 reference key,
HeapAnalyzer找到KeyedWeakReference,定位内存泄露。 -
HeapAnalyzer计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。 -
内存泄漏信息送回给
DisplayLeakService,它是运行在 app 进程里的一个服务。然后通过一个独立的页面显示出来<activity android:name="com.squareup.leakcanary.internal.DisplayLeakActivity" android:enabled="false" android:icon="@drawable/leak_canary_icon" android:label="@string/leak_canary_display_activity_label" android:taskAffinity="com.squareup.leakcanary" android:theme="@style/leak_canary_LeakCanary.Base" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
集成步骤
首先引入jar包
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
}
自定义Appliacation并配置
private static RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) { return refWatcher;}public void onCreate() { super.onCreate();refWatcher = LeakCanary.install(this); }
LeakCanary 自动检测 Activity 泄漏只支持 Android ICS 以上版本。因为 Application.registerActivityLifecycleCallbacks() 是在 API 14 引入的。如果要在 ICS 之前监测 Activity 泄漏,可以重载 Activity.onDestroy() 方法,然后在这个方法里调用 RefWatcher.watch(this) 来实现。
注意:LeakCanary 自动检测 Activity 泄漏只支持 Android ICS 以上版本。因为 Application.registerActivityLifecycleCallbacks() 是在 API 14 引入的。如果要在 ICS 之前监测 Activity 泄漏,可以重载 Activity.onDestroy() 方法,然后在这个方法里调用 RefWatcher.watch(this) 来实现。
使用 RefWatcher 监控 Fragment:
public class BaseFragment extends Fragment {
@Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = KuzhuanApplication.getRefWatcher(getActivity()); refWatcher.watch(this); }}
监控其他泄漏
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); refWatcher.watch(someObjNeedGced);
当 someObjNeedGced 还在内存中时,就会在 logcat 里看到内存泄漏的提示。
自定义UI样式
res/
drawable-hdpi/
__leak_canary_icon.png
drawable-mdpi/
__leak_canary_icon.png
drawable-xhdpi/
__leak_canary_icon.png
drawable-xxhdpi/
__leak_canary_icon.png
drawable-xxxhdpi/
__leak_canary_icon.png
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="__leak_canary_display_activity_label">MyLeaks</string> </resources>
保存 leak trace
在 APP 的目录中,DisplayLeakActivity 保存了 7 个 dump 文件和 leak trace。你可以在你的 APP 中,定义R.integer.__leak_canary_max_stored_leaks 来覆盖类库的默认值。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="__leak_canary_max_stored_leaks">20</integer>
</resources>
如何导出 hprof 文件
File heapDumpFile = new File("heapdump.hprof"); Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
可以参阅 AndroidHeapDumper.java 的代码。
上传 leak trace 到服务器
你可以改变处理完成的默认行为,将 leak trace 和 heap dump 上传到你的服务器以便统计分析。
创建一个 LeakUploadService, 最简单的就是继承 DisplayLeakService :
public class LeakUploadService extends DisplayLeakService {
@Override
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
if (!result.leakFound || result.excludedLeak) {
return;
}
myServer.uploadLeakBlocking(heapDump.heapDumpFile, leakInfo);
}
}
请确认 release 版本 使用 RefWatcher.DISABLED:
public class ExampleApplication extends Application {
public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}
private RefWatcher refWatcher;
@Override public void onCreate() {
super.onCreate();
refWatcher = installLeakCanary();
}
protected RefWatcher installLeakCanary() {
return RefWatcher.DISABLED;
}
}
自定义 RefWatcher:
public class DebugExampleApplication extends ExampleApplication {
protected RefWatcher installLeakCanary() {
return LeakCanary.install(app, LeakUploadService.class);
}
}
别忘了注册 service:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
<application android:name="com.example.DebugExampleApplication">
<service android:name="com.example.LeakUploadService" />
</application>
</manifest>
有时,leak trace 不够,你需要通过 MAT深挖 dump 文件。
通过以下方法,你能找到问题所在:
- 查找所有的
com.squareup.leakcanary.KeyedWeakReference实例。 - 检查
key字段 - Find the
KeyedWeakReferencethat has akeyfield equal to the reference key reported by LeakCanary. - 找到 key 和 和 logcat 输出的 key 值一样的
KeyedWeakReference。 referent字段对应的就是泄露的对象。- 剩下的,就是动手修复了。最好是检查到 GC root 的最短强引用路径开始。
更多leakcanary相关 请移步:https://github.com/square/leakcanary/wiki/FAQ

本文介绍LeakCanary的原理及集成步骤,包括如何监控Activity和Fragment的内存泄漏,自定义UI样式,导出hprof文件,上传leaktrace到服务器等高级功能。
1459

被折叠的 条评论
为什么被折叠?



