LeakCanary 是什么?
LeakCanary是Square公司基于MAT开源的一个工具,用来检测Android App中的内存泄露问题。官方地址:https://github.com/square/leakcanary
工作原理分析:
1.怎么检测内存泄漏?什么时候去检测内存泄漏?
//注册生命周期 在onDestory 的时候 观察Activity 对象
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
//开始观察
refWatcher.watch(activity);
}
};
// 此方法为停止观察activity 也就是不在监听回掉
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
自己去跟源码就会发现,LeakCanary 会 注册一个ActivityLifecycleCallbacks ,当监听到activity Ondestory 的时候,就会去观察这个activity 是不是泄漏了,有没有被GC回收。
具体怎么观察Activity 有没有被回收?
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
我们看到,观察activity 就是 把 activity 放到WeakRefence 里面,WeakReference 的第二个参数是一个Qeue 对象,我们手动执行一下GC,然后Qeue.pull 一下,看看Qeue 里面是不是有WeakRefence ,如果有说明对象被回收了。那么这个activity 就没有泄漏,否则就有可能泄露了导致回收不了。
这个机制是WeakReference 的机制。有兴趣可以自行了解一下。
怎么执行GC?
Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
上面diam执行之后,我们再看下内存,还有没有activity 的实例,如果还有,那么说明这个activity 可能被泄露了。我们需要根据hprof 文件看下具体的调用链。
怎么生成hprof?
如果activity 内存泄露了,我们需要hprof 文件去分析。那么怎么拿到hprof?
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
怎么分析hprof
使用了下面的开源项目:
implementation 'com.squareup.haha:haha:2.0.4'
分析完之后,用界面显示出来就没有什么好说了。
怎么在桌面有两个图标?
<activity
android:label="@string/app_name"
android:name=".MainActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name=".internal.DisplayLeakActivity"
android:process=":leakcanary"
android:enabled="false"
android:label="@string/leak_canary_display_activity_label"
android:icon="@mipmap/leak_canary_icon"
android:taskAffinity="com.squareup.leakcanary.${applicationId}"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
我们发现,leackCanary会生成一个自己的桌面图标,点击进去可以查看内漏泄露的信息,而且不需要创建桌面快捷方式的权限,就可以做到,他是用上面的方式做到的。清单里面可以有多个"android.intent.category.LAUNCHER",当然也会有一个默认的。
怎么设置组件启用和关闭
public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
boolean enabled) {
ComponentName component = new ComponentName(appContext, componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
也就这样啦,没有具体怎么分析hprof,有兴趣可以再深入研究了。