使用
app的build.gradle配置
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
Application配置
@Override
public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
分析
从LeakCanary.install(this)入手,先看看install(this)方法
LeakCanary.java
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);
}
该处使用了构造者的设计模式,并采用了Android的的Builder方式构建,主要是初始化一些参数,我们先来看看最后一个buildAndInstall方法
AndroidRefWatcherBuilder.java
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
}
return refWatcher;
}
该处build()调用了父类RefWatcherBuilder的build方法,初始化默认的参数,但AndroidRefWatcherBuilder实现了这鞋默认参数,我们来看看有哪些
//dump内存泄漏处的heap信息,写入hprof文件
@Override protected HeapDumper defaultHeapDumper() {
LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
return new AndroidHeapDumper(context, leakDirectoryProvider);
}
//判断是否处于调试模式,调试模式中不会进行内存泄漏检测
@Override protected DebuggerControl defaultDebuggerControl() {
return new AndroidDebuggerControl();
}
//解析完 hprof 文件并通知 DisplayLeakService 弹出提醒
@Override protected HeapDump.Listener defaultHeapDumpListener() {
return new ServiceHeapDumpListener(context, DisplayLeakService.class);
}
// 排除可以忽略的泄漏路径
@Override protected ExcludedRefs defaultExcludedRefs() {
return AndroidExcludedRefs.createAppDefaults().build();
}
//线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
@Override protected WatchExecutor defaultWatchExecutor() {
return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
}
然后继续看buildAndInstall方法,判断处为true,看看LeakCanary.enableDisplayLeakActivity(context)方法,看名字,感觉他是显示可见的Activity
LeakCanary.java
public static void enableDisplayLeakActivity(Context context) {
setEnabled(context, DisplayLeakActivity.class, true);
}
然后会调用LeakCanaryInternals.setEnabled的setEnabled方法,然后又异步的使用线程池的方式调用了setEnabledBlocking方法,
//这个方法主要设置DisplayLeakActivity是否可见
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);
}
上面的方法主要是来控制DisplayLeakActivity是否显示到桌面上,也就是那个显示到桌面上的小黄色的图标,我们可以来看看他的清单文件
<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>
主要看intent-filter里面的action和category,这种方式的设置,就是显示小图标的小技巧,这个小图标的生命是随我们的app的,我们的app被卸载了,这个小图标也会不存在。
然后我们继续看buildAndInstall方法。
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
ActivityRefWatcher.java
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
if (SDK_INT < ICE_CREAM_SANDWICH) {
// If you need to support Android < ICS, override onDestroy() in your base activity.
return;
}
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
activityRefWatcher.watchActivities();
}
public void watchActivities() {
// unregisterActivityLifecycleCallbacks 取消ActivityLifecycle的注册
stopWatchingActivities();
//注册Activity的生命周期,监听Activity的启动和销毁
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
这个地方比较简单,就是给Application注册一个监听Activity的启动和销毁,然后再Activity关闭的时候,被onActivityDestroyed监听到,然后调用onActivityDestroyed方法
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
然后我们继续看watch方法,将这个Activity带过去观察
RefWatcher.java
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
public void watch(Object watchedReference, String referenceName) {
...
final long watchStartNanoTime = System.nanoTime();
//通过uuid生成唯一的一个key
String key = UUID.randomUUID().toString();
//放到set集合
retainedKeys.add(key);
//弱引用对象,包裹一些参数,这里面有个ReferenceQueue queue对象,主要功能就是KeyedWeakReference如果被回收了,会被添加到这个ReferenceQueue中
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
继续看ensureGoneAsync方法,
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
这个地方WatchExecutor来执行execute方法,WatchExecutor是我们在上面AndroidRefWatcherBuilder初始化的,他是AndroidWatchExecutor,而且在创建AndroidWatchExecutor对象的时候,传递了一个5秒的一个参数过来,然后我们来看看这个类。
AndroidWatchExecutor
//initialDelayMillis是build构建的时候传递进来的参数,为5秒
public AndroidWatchExecutor(long initialDelayMillis) {
//创建一个活动在主线程的Handler
mainHandler = new Handler(Looper.getMainLooper());
//创建一个活动在子线程的Handler
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
//执行的方法
@Override public void execute(Retryable retryable) {
//这个地方会进行判断,如果当前是在主线程环境下的话,直接调用waitForIdle
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
//如果当前是在子线程环境下的话,会调用postWaitForIdle方法,
//然后又通过主线程的Handler,继续切换到主线程,调用waitForIdle方法,
//从这个地方就可以看出,execute的执行,在调用waitForIdle方法时,必须是在主线程上面运行
postWaitForIdle(retryable, 0);
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
//这个地方又用到了一个Handler的小技巧addIdleHandler方法,该方法主要是当Handler在空闲的时候执行操作
void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
//计算出延迟执行postWaitForIdle的时间,然后继续检测。
//大致可理解为,Handler空闲了,我就每隔几秒去循环检测。
//检测的执行就是调用Retryable.Result的run方法,该方法已经被在RefWatcher的ensureGoneAsync实现了。
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
//执行retryable.run()方法
Retryable.Result result = retryable.run();
//如果run执行的结果是RETRY的话,则继续监测,否则,就停止监测
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
然后我们继续回到RefWatcher的ensureGoneAsync方法
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
return返回执行ensureGone方法,然后我们继续看,这个地方得单独领出来一个一个的讲
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)) {
//直接返回完成,AndroidWatchExecutor循环监测停止
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);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
removeWeaklyReachableReferences();
循环遍历ReferenceQueue队列,然后取出KeyedWeakReference,如果不为空,就从KeyedWeakReference拿到key字段,然后将他从set集合中移除
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
gone(reference)
上面已经进行了一次遍历移除操作,然后继续判断这个KeyedWeakReference的key是否还存在在set集合中,如果不存在在set集合中,说明已经被移除了,也就是说在上一次remove操作中,queue.poll()不为null,意味着gc回收了这个引用,还记得ReferenceQueue的作用吗,记录被回收的引用
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
gcTrigger.runGc()
为了再次验证,手动进行一次gc操作
@Override public void runGc() {
Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
}
removeWeaklyReachableReferences();
再一次遍历ReferenceQueue队列
if (!gone(reference))
再一次判断refrence实例是否被回收了,如果还没被回收,就进行分析泄漏原因。
File heapDumpFile = heapDumper.dumpHeap();
来看下heapDumper.dumpHeap(),heapDumper对象是在build的时候初始化的,他是AndroidHeapDumper类
AndroidHeapDumper
@Override
public File dumpHeap() {
//创建hprof文件,具体可以看DefaultLeakDirectoryProvider的newHeapDumpFile方法
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
该端代码主要功能就是创建一个hprof文件,然后调用 Debug.dumpHprofData() 方法 dump 当前堆内存并写入刚才创建的文件。然后继续看上面ensureGone方法
heapdumpListener.analyze
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
new HeapDump比较简单,就是一个包装类,将这个参数保存放到这个HeapDump类里面。
heapdumpListener是在build的时候初始化的ServiceHeapDumpListener类,具体可以看AndroidRefWatcherBuilder类中的defaultHeapDumpListener方法,该方法带了两个参数:context和DisplayLeakService.class
然后我们来看ServiceHeapDumpListener类的analyze方法
public ServiceHeapDumpListener(Context context,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabled(context, listenerServiceClass, true);
setEnabled(context, HeapAnalyzerService.class, true);
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
listenerServiceClass就是 DisplayLeakService,然后我们继续看 HeapAnalyzerService.runAnalysis方法
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
context.startService(intent);
}
public HeapAnalyzerService() {
super(HeapAnalyzerService.class.getSimpleName());
}
@Override protected void onHandleIntent(Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
runAnalysis方法主要作用是开启一个DisplayLeakService服务,然后DisplayLeakService是一个IntentService类,会执行onHandleIntent方法,intent拿到DisplayLeakService和HeapDump类。
然后初始化HeapAnalyzer,调用checkForLeak进行heapDumpFile文件分析,然后将分析的结果信息返回给AnalysisResult,然后看sendResultToListener方法
AbstractAnalysisResultService.java
public static void sendResultToListener(Context context, String listenerServiceClassName,
HeapDump heapDump, AnalysisResult result) {
Class<?> listenerServiceClass;
try {
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Intent intent = new Intent(context, listenerServiceClass);
intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
intent.putExtra(RESULT_EXTRA, result);
context.startService(intent);
}
把DisplayLeakService服务传过来传过去的,终于要开启他了,然后我们看DisplayLeakService类
//DisplayLeakService没有初始化操作,只能来看看父类了
public class DisplayLeakService extends AbstractAnalysisResultService
//是一个IntentService,那我们可以来看onHandleIntent方法
public abstract class AbstractAnalysisResultService extends IntentService
AbstractAnalysisResultService.java
@Override protected final void onHandleIntent(Intent intent) {
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try {
onHeapAnalyzed(heapDump, result);
} finally {
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
}
}
拿到dump分析的结果信息,然后调用onHeapAnalyzed方法,onHeapAnalyzed是一个抽象方法,那么,我们又得回到子类DisplayLeakService去看下onHeapAnalyzed方法。分析完后,finally最后会删除这个分析文件
DisplayLeakService.java
@Override
protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
...
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
...
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo);
...
}
这个里面不重要的东西我就去掉了,很多操作都是在构建通知的信息。
DisplayLeakActivity.createPendingIntent主要是构建通知栏点击打开的Activity,也就是DisplayLeakActivity。
然后,这个地方就是我们平时比较熟知的了,在发生泄漏操作的时候,会显示通知栏。
afterDefaultHandling是一个抽象方法,看注释说明,我们可以去重写这个方法,然后实现我们自己逻辑的一些自定义处理,比如上传到我们自己的服务器。