Android LeakCanary的使用和原理,春招我借这份PDF的复习思路

}

public class RefWatcherBuilder<T extends RefWatcherBuilder> {

/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}

}

然后调用excludedRefs方法添加白名单,在AndroidExcludedRefs枚举类中定义了忽略列表信息,如果这些列表中的类发生了内存泄露,并不会显示出来,同时HeapAnalyzer计算GCRoot强引用路径,也会忽略这些类,如果你希望自己项目中某个类泄露的,但是不希望他显示,就可以把类添加到这个上面

public enum AndroidExcludedRefs {

// ######## Android SDK Excluded refs ########

ACTIVITY_CLIENT_RECORD__NEXT_IDLE(SDK_INT >= KITKAT && SDK_INT <= LOLLIPOP) {
@Override void add(ExcludedRefs.Builder excluded) {
excluded.instanceField(“android.app.ActivityThread$ActivityClientRecord”, “nextIdle”)
.reason(“Android AOSP sometimes keeps a reference to a destroyed activity as a”

  • " nextIdle client record in the android.app.ActivityThread.mActivities map."
  • " Not sure what’s going on there, input welcome.");
    }
    }

    }

最后调用了buildAndInstall方法,创建了一个RefWatcher对象并返回,这个对象是用于检测是否有对象未被回收导致的内存泄露

/**

  • Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
    */
    public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
    LeakCanary.enableDisplayLeakActivity(context);
    ActivityRefWatcher.install((Application) context, refWatcher);
    }
    return refWatcher;
    }

因为分析泄露是在另一个进程进行的,所以判断当前启动的Application是否在分析内存泄露的进程中,如果是就直接返回DISABLED,不在进行后续初始化,如果发现是在程序主进程中,就进行初始化

LeakCanary.enableDisplayLeakActivity(context);主要作用是调用PackageManagerDisplayLeakActivity设置为可用。

public static void enableDisplayLeakActivity(Context context) {
setEnabled(context, DisplayLeakActivity.class, true);
}

public static void setEnabled(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
executeOnFileIoThread(new Runnable() {
@Override public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}

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);
}

从配置文件看LeakCanary这几个文件都是运行在新进程的,DisplayLeakActivity默认enable=false,这样就可以一开始隐藏启动图标

接着 ActivityRefWatcher.install((Application) context, refWatcher);这里把refWatcher当做参数传入,同时对Activity的生命周期进行了监听

public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}

public void watchActivities() {
// Make sure you don’t get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}

public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(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);
}
};

void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}

首先就是注册Activity的生命周期的监听器lifecycleCallbacks,这个监听器可以监听项目中所有Activity的的生命周期,然后在Activity销毁调用onActivityDestroyed的时候,LeakCanary就会获取这个Activity,然后对其进行分析,看是否有内存泄露

分析内存泄露

这里分析对象是否内存泄露的是RefWatcher类,下面简单介绍一下这个类的成员变量

  • WatchExecutor watchExecutor:确保任务在主线程进行,同时默认延迟5s执行任务,留时间给系统GC
  • DebuggerControl debuggerControl:控制中心
  • GcTrigger gcTrigger:内部调用Runtime.getRuntime().gc(),手动触发GC
  • HeapDumper heapDumper:用于创建.hprof文件,用于储存head堆快照,可以知道哪些程序在大部分使用内存
  • HeapDump.Listener heapdumpListener:分析结果完成后会告诉这个监听器
  • ExcludedRefs excludedRefs:分析内存泄露的白名单

从上面可以看出,每当Activity销毁,就会调用RefWatcherwatch方法,去分析是否是内存泄露

public void watch(Object watchedReference) {
watch(watchedReference, “”);
}

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);
}

上面代码主要作用是,先生成一个随机数key放在retainedKeys容器里,用来区分对象是否被回收,创建了一个弱引用,然后把要分析的Activity对象存入,然后调用了ensureGoneAsync方法

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}

然后用watchExecutor去调度分析任务,这个主要是保证,在主线程进行,延迟5s,让系统有时间GC

@SuppressWarnings(“ReferenceEquality”) // Explicitly checking for named null.
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);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}

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);
}
}

private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}

首先通过removeWeaklyReachableReferences()方法,尝试从弱引用队列获取待分析对象,如果不为空说明被系统回收了,就把retainedKeys中的key值移除,如果被系统回收直接返回DONE,如果没有被系统回收,就手动调用 gcTrigger.runGc();手动触发系统gc,然后再次调用removeWeaklyReachableReferences()方法,如过还是为空,则判断为内存泄露

GcTrigger DEFAULT = new GcTrigger() {
@Override public void runGc() {
// Code taken from AOSP FinalizationTest:
// https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
// java/lang/ref/FinalizationTester.java
// System.gc() does not garbage collect every time. Runtime.gc() is
// more likely to perfom a gc.
Runtime.getRuntime().gc();
enqueueReferences();
System.runFinalization();
}

private void enqueueReferences() {
// Hack. We don’t have a programmatic way to wait for the reference queue daemon to move
// references to the appropriate queues.
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new AssertionError();
}
}
};

手动触发GC后,调用enqueueReferences方法沉睡100ms,给系统GC时间, System.runFinalization();,这个是强制调用已失去引用对象的finalize方法

确定有内存泄漏后,调用heapDumper.dumpHeap();生成.hprof文件,然后回调到heapdumpListener监听,这个监听实现是ServiceHeapDumpListener类,会调analyze()方法

public final class ServiceHeapDumpListener implements HeapDump.Listener {

@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, “heapDump”);
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
}

HeapDump是一个modle类,里面用于储存一些分析类强引用的需要信息 HeapAnalyzerService.runAnalysis方法是发送了一个intent,启动了HeapAnalyzerService服务,这是一个intentService

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);
}

启动服务后,会在onHandleIntent方法启动分析,找到内存泄露的引用关系

@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);
}

找到引用关系

  • 启动分析内存泄露的服务后,会实例化一个HeapAnalyzer对象,然后调用checkForLeak方法来分析最终得到的结果,
  • checkForLeak这里用到了Square的另一个库haha,哈哈哈哈哈,名字真的就是叫这个,开源地址:github.com/square/haha…
  • 得到结果后调用AbstractAnalysisResultService.sendResultToListener()方法,这个方法启动了另一个服务

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);
}

listenerServiceClassName就是开始LeakCanary.install方法传入的DisplayLeakService,它本身也是一个intentService

@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();
}
}

然后调用自身的onHeapAnalyzed方法

protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = LeakCanary.leakInfo(this, heapDump, result, true);
CanaryLog.d(“%s”, new Object[]{leakInfo});
boolean resultSaved = false;
boolean shouldSaveResult = result.leakFound || result.failure != null;
if(shouldSaveResult) {
heapDump = this.renameHeapdump(heapDump);
resultSaved = this.saveResult(heapDump, result);
}

PendingIntent pendingIntent;
String contentTitle;
String contentText;
// 设置消息通知的 pendingIntent/contentTitle/contentText

int notificationId1 = (int)(SystemClock.uptimeMillis() / 1000L);
LeakCanaryInternals.showNotification(this, contentTitle, contentText, pendingIntent, notificationId1);
this.afterDefaultHandling(heapDump, result, leakInfo);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

image

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

image

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

[外链图片转存中…(img-1HP3OrsR-1712339026962)]

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

[外链图片转存中…(img-CHU8FQVP-1712339026962)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值