Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx

上传人:b****8 文档编号:28869541 上传时间:2023-07-20 格式:DOCX 页数:24 大小:90.60KB
下载 相关 举报
Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx_第1页
第1页 / 共24页
Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx_第2页
第2页 / 共24页
Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx_第3页
第3页 / 共24页
Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx_第4页
第4页 / 共24页
Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx

《Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx》由会员分享,可在线阅读,更多相关《Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx(24页珍藏版)》请在冰豆网上搜索。

Android 源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下.docx

Android源码系列之十四从源码的角度深入理解LeakCanary的内存泄露检测机制下

Android源码系列之<十四>从源码的角度深入理解LeakCanary的内存泄露检测机制(下)

LeakCanary的相关配置信息。

根据上篇文章的介绍我们知道LeakCanary为了不给APP进程造成影响所以新开启了一个进程,在新开启的进程中做内存泄露检测,这篇文章将要带领小伙伴们从源码的角度出发深入了解一下LeakCanary的内存泄露检测机制,希望能给小伙伴们一点帮助,如果你对LeakCanary的原理非常熟悉了,请跳过本文(*^__^*)……

当在项目中引入了LeakCanary后,就是进行LeakCanary的安装操作,初始化操作流程如下所示:

[java]viewplaincopy

publicclassExampleApplicationextendsApplication{

@Override

publicvoidonCreate(){

super.onCreate();

//判断是否是LeakCanary的分析进程

if(LeakCanary.isInAnalyzerProcess(this)){

//ThisprocessisdedicatedtoLeakCanaryforheapanalysis.

//Youshouldnotinityourappinthisprocess.

return;

}

//初始化LeakCanary

LeakCanary.install(this);

}

}

安装LeakCanary前先判断当前进程是否是HeapAnalyzerService所在的远程分析进程,如果是分析进程就直接返回因为分析进程只是来分析堆信息是否存在内存泄露,否则调用LeakCanary的install()方法,该方法就是入口,我们跟进去,源码如下:

[java]viewplaincopy

/**

*Createsa{@linkRefWatcher}thatworksoutofthebox,andstartswatchingactivity

*references(onICS+).

*/

publicstaticRefWatcherinstall(Applicationapplication){

returnrefWatcher(application).listenerServiceClass(DisplayLeakService.class)

.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())

.buildAndInstall();

}

install()方法内部通过链式调用最终返回了一个RefWatcher对象,该对象就是来监听哪些对象是否发生内存泄露的,refWatcher()方法返回AndroidRefWatcherBuilder对象以后都是调用该对象的链式方法,其中excludeRefs()方法表示排除掉由AndroidSDK引发的内存泄露,因为AndroidSDK引发的内存泄露并非我们程序造成如果检测到是AndroidSDK引发的就不会报告给用户(如果想详细了解SDK引发的内存泄露信息可自行查看AndroidExcludedRefs类,该类有详细说明)。

最后调用的是buildAndInstall()方法,该方法源码如下:

[java]viewplaincopy

/**

*Createsa{@linkRefWatcher}instanceandstartswatchingactivityreferences(onICS+).

*/

publicRefWatcherbuildAndInstall(){

RefWatcherrefWatcher=build();

if(refWatcher!

=DISABLED){

LeakCanary.enableDisplayLeakActivity(context);

ActivityRefWatcher.installOnIcsPlus((Application)context,refWatcher);

}

returnrefWatcher;

}

buildAndInstall()方法中调用build()方法获取一个RefWatcher对象refWatcher并最终把refWatcher返回,当时在返回之前做了一个判断,若refWatcher不是DISABLED对象时就先后调用了LeakCanary的静态enableDisplayLeakActivity()方法和ActivityRefWatcher的静态installOnIcsPlus()方法。

为什么要判断refWatcher对象是不是DISABLED呢?

是因为在打包的时候如果当前模式是DEBUG模式那么refWatcher就不是DISABLED,否则就是DISABLED。

这是间接的判断打包模式来决定时候安装LeakCanary库。

LeakCanary的静态方法enableDisplayLeakActivity()是动态的设定DisplayLeakActivity为可用状态,因为在上篇文章中介绍过DisplayLeakActivity默认是不可用的。

ActivityRefWatcher的installOnIcsPlus()方法是真正的安装LeakCanary库的,该方法源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicstaticvoidinstallOnIcsPlus(Applicationapplication,RefWatcherrefWatcher){

if(SDK_INT

//如果当前SDK版本号小于4.0则直接返回

//IfyouneedtosupportAndroid

return;

}

ActivityRefWatcheractivityRefWatcher=newActivityRefWatcher(application,refWatcher);

activityRefWatcher.watchActivities();

}

installOnIcsPlus()方法先判断当前SDK版本号,如果SDK版本号低于4.0则什么都不做就直接返回了,那也就是说LeakCanary库在Android4.0之前是没法直接使用的,若要在4.0之前使用就需要在基类BaseActivity的onDestroy()方法中调用RefWatcher的watch()方法来监控。

接着是创建了一个ActivityRefWatcher实例对象并调用该对象的watchActivities()方法,根据方法名字就知道是监控Activity的,跟进源码看一下该方法,源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

publicvoidwatchActivities(){

//Makesureyoudon'tgetinstalledtwice.

//开始监控Activity之前先尝试移除已经添加过的回调,确保只添加一次

stopWatchingActivities();

application.registerActivityLifecycleCallbacks(lifecycleCallbacks);

}

publicvoidstopWatchingActivities(){

//移除Application中已经添加的回调接口

application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);

}

通过阅读watchActivities()方法发现原来LeakCanary是巧妙的利用了Android4.0之后的API,因为在4.0之后Google给Application添加了Activity的生命周期回调接口,如果我们注入了该回调接口,那么当Activity的声明周期发生变化的时候就会回调相关方法。

我们看一下注入的回调接口lifecycleCallbacks,源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privatefinalApplication.ActivityLifecycleCallbackslifecycleCallbacks=

newApplication.ActivityLifecycleCallbacks(){

@Override

publicvoidonActivityCreated(Activityactivity,BundlesavedInstanceState){

}

@Override

publicvoidonActivityStarted(Activityactivity){

}

@Override

publicvoidonActivityResumed(Activityactivity){

}

@Override

publicvoidonActivityPaused(Activityactivity){

}

@Override

publicvoidonActivityStopped(Activityactivity){

}

@Override

publicvoidonActivitySaveInstanceState(Activityactivity,BundleoutState){

}

@Override

publicvoidonActivityDestroyed(Activityactivity){

//每当Activity销毁时系统都会回调该方法

ActivityRefWatcher.this.onActivityDestroyed(activity);

}

};

在lifecycleCallbacks()回调方法中仅仅在onActivityDestroyed()方法中回调了ActivityRefWatcher的onActivityDestroyed()方法并把要销毁的Activity传递进来,因此我们可以猜测当Activity被销毁的时候都会触发onActivityDestroyed()方法,LeakCanary就是通过监控传递进来的Activity通过判断它的应用关系来检测是否是发生了内存泄露。

到这里我们已经知道了LeakCanary检测Activity的时机,接下来看看LeakCanary是如何判断Activity存在内存泄露的。

onActivityDestroyed()方法就是检测Activity是否发生内存泄露的,其源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

voidonActivityDestroyed(Activityactivity){

refWatcher.watch(activity);

}

onActivityDestroyed()方法中辗转调用了refWatcher的watch()方法(该方法可以监控所有的Java对象),watch()方法又调用其重载方法,代码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

/**

*WatchestheprovidedreferencesandchecksifitcanbeGCed.Thismethodisnonblocking,

*thecheckisdoneonthe{@linkWatchExecutor}this{@linkRefWatcher}hasbeenconstructed

*with.

*

*@paramreferenceNameAnlogicalidentifierforthewatchedobject.

*/

publicvoidwatch(ObjectwatchedReference,StringreferenceName){

if(this==DISABLED){

return;

}

checkNotNull(watchedReference,"watchedReference");

checkNotNull(referenceName,"referenceName");

finallongwatchStartNanoTime=System.nanoTime();

Stringkey=UUID.randomUUID().toString();

retainedKeys.add(key);

finalKeyedWeakReferencereference=

newKeyedWeakReference(watchedReference,key,referenceName,queue);

ensureGoneAsync(watchStartNanoTime,reference);

}

watch()方法中先是校验当前程序是否是DEBUG模式,如果是就直接返回否则对传递进来的参数进行非空校验,校验通过之后通过生成一个唯一的UUID并存储在retainKeys中,然后根据这个UUID初始化一个KeyedWeakReference实例对象reference,reference中保留了当前UUID和name,然后调用了ensureGoneAsync()方法,该方法源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privatevoidensureGoneAsync(finallongwatchStartNanoTime,finalKeyedWeakReferencereference){

watchExecutor.execute(newRetryable(){

@Override

publicRetryable.Resultrun(){

returnensureGone(reference,watchStartNanoTime);

}

});

}

ensureGoneAsync()方法中调用了watchExecutor的execute()方法,经过分析watchExecutor是AndroidWatchExecutor的实例对象,也就是说最终执行的是AndroidWatchExecutor的execute()方法,我们进入该方法看看:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

@Override

publicvoidexecute(Retryableretryable){

//确保在主线程中执行

if(Looper.getMainLooper().getThread()==Thread.currentThread()){

waitForIdle(retryable,0);

}else{

postWaitForIdle(retryable,0);

}

}

在AndroidWatchExecutor的execute()方法中首先判断当前线程是不是主线程,如果是主线程就直接调用waitForIdle()方法,否则调用postWaitForIdle()方法切换到主线后再执行waitForIdle()方法。

由此可见execute()方法是保证操作在主线程中进行,我们进入waitForIdle()方法看一下,源码如下所示:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

voidwaitForIdle(finalRetryableretryable,finalintfailedAttempts){

//Thisneedstobecalledfromthemainthread.

//当前方法只能在主线程中调用

Looper.myQueue().addIdleHandler(newMessageQueue.IdleHandler(){

@Override

publicbooleanqueueIdle(){

postToBackgroundWithDelay(retryable,failedAttempts);

returnfalse;

}

});

}

waitForIdle()方法中通过调用Looper的myQueue()方法拿到当前主线程的消息队列MessageQueue对象,接着调用MessageQueue对象的addIdleHandler()方法给当前消息队列添加一个IdleHandler实例对象,IdleHandler是一个接口,该接口中定义的queueIdle()方法的返回值为boolean类型,当返回true表示继续保持IdleHandler实例对象否则移除该实例对象。

需要说明的是:

addIdleHandler()方法的目的是给当前消息队列添加一个回调接口,当当前消息队列中没有可执行的消息时就会回调该IdleHandler的queueIdle()方法,说白了就是等到主线程空闲的时候就会回调queueIdle()方法,queueIdle()方法返回了false表明该方法只被执行一次。

在queueIdle()方法中调用了postToBackgroundWithDelay()方法,我们跟进postToBackgroundWithDelay()方法中看一下,该方法源码如下:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

privatevoidpostToBackgroundWithDelay(finalRetryableretryable,finalintfailedAttempts){

longexponentialBackoffFactor=(long)Math.min(Math.pow(2,failedAttempts),maxBackoffFactor);

longdelayMillis=initialDelayMillis*exponentialBackoffFactor;

backgroundHandler.postDelayed(newRunnable(){

@Override

publicvoidrun(){

//切换到子线程中执行,retryable.run()方法

Retryable.Resultresult=retryable.run();

if(result==RETRY){

//如果retryable的run方法返回类型为RETRY,则循环以上流程

postWaitForIdle(retryable,failedAttempts+1);

}

}

},delayMillis);

}

postToBackgroundWithDelay()方法又切换到切换到子线程中执行Retryable的run()方法,如果run()方法返回值为RETRY就继续调用postWaitForIdle()方法循环以上流程否则本次调用结束,由于Retryable的run()方法中调用的是RefWatcher的ensureGone()方法,我们进入该方法看一下,其源码如下所示:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

Retryable.ResultensureGone(finalKeyedWeakReferencereference,finallongwatchStartNanoTime){

longgcStartNanoTime=System.nanoTime();

longwatchDurationMs=NANOSECONDS.toMillis(gcStartNanoTime-watchStartNanoTime);

//尝试移除

removeWeaklyReachableReferences();

if(debuggerControl.isDebuggerAttached()){

//Thedebuggercancreatefalseleaks.

returnRETRY;

}

//gone()方法检测是否包含当前reference

if(gone(reference)){

returnDONE;

}

//触发垃圾回收器进行回收操作

gcTrigger.runGc();

//再次尝试移除操作

removeWeaklyReachableReferences();

//若gone()方法返回false,表示有内存泄露

if(!

gone(reference)){

longstartDumpHeap=System.nanoTime();

longgcDurationMs=NANOSECONDS.toMillis(startDumpHeap-gcStartNanoTime);

//把堆内存相关信息映射成文件

FileheapDumpFile=heapDumper.dumpHeap();

if(heapDumpFile==RETRY_LATER){

//Couldnotdumptheheap.

returnRETRY;

}

longheapDumpDurationMs=NANOSECONDS.toMillis(System.nanoTime()-startDumpHeap);

//分析堆内存的映射文件,判断是否发生内存泄露

heapdumpListener.analyze(

newHeapDump(heapDumpFile,reference.key,reference.name,excl

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 职业教育 > 职业技术培训

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1