ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android LeakCanary的使用和原理,移动端app开发框架

2022-01-27 12:59:06  阅读:174  来源: 互联网

标签:LeakCanary app heapDump void new intent Android public result


.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);
}
mp, 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);
}

标签:LeakCanary,app,heapDump,void,new,intent,Android,public,result
来源: https://blog.csdn.net/m0_66145060/article/details/122715874

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有