LeakCanary 1.6.2 源码解析

创建一个refWatcher,启动一个ActivityRefWatcher,通过actvityLifecyclerCallbacks 把Activity的onDestory生命周期关联,最后在线程池中去开始分析对象的引用。

原理

当Activity或者Fragment销毁时,启动一个ActivityRefWatcher,通过actvityLifecyclerCallbacks 关联onDestoryed()方法。用一个弱引用去包装当前销毁的Activity或者Fragment,并且将它与本地的一个ReferenceQueue队列关联。我们知道如果GC触发了,系统会将当前的引用对象存入队列中。如果没有被回收,队列中则没有当前的引用对象。所以LeakCanary会去判断,ReferenceQueue是否有当前观察的Activity或者Fragment的引用对象,第一次判断如果不存在,就去手动触发一次GC,然后做第二次判断,如果还是不存在,则表明出现了内存泄漏。

ReferenceQueue对象

引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中。就是当一个对象被回收时,会把引用对象放入到ReferenceQueue中,内部是一个链表队列。

1. install

LeakCanary在使用上会调用install方法在application注册,从install开始入手。
分四部:

  1. refWatcher()
    创建一个AndroidRefWatcherBuilder对象,建造者模式的Builder对象,为了创建AndroidRefWatcher对象。AndroidRefWatcher 属于观察者对象。
  2. listenerServiceClass()
  3. excludedRefs()
  4. buildAndInstall() 构造AndroidRefWatcher对象,注册activity的销毁监听,创建弱引用包装要回收的对象,检测是否被回收,如果始终无法回收,产生分析文件和日志。
 @NonNull
    public static RefWatcher install(@NonNull Application application) {
        return ((AndroidRefWatcherBuilder)refWatcher(application)
        .listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()))
        .buildAndInstall();
    }

2.refWatcher

  @NonNull
    public static AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
        return new AndroidRefWatcherBuilder(context);
    }
  AndroidRefWatcherBuilder(@NonNull Context context) {
        this.context = context.getApplicationContext();
    }

3.listenerServiceClass

在调用listenerServiceClass方法的时候传入了一个DisplayLeakService.class。
最后调用了heapDumpListener监听器,字母意思是堆存贮监听器,同时接收了一个ServiceHeapDumpListener实例对象。同时接收外部的DisplayLeakService对象。

   @NonNull
    public AndroidRefWatcherBuilder listenerServiceClass(@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        return (AndroidRefWatcherBuilder)this.heapDumpListener(new ServiceHeapDumpListener(this.context, listenerServiceClass));
    }

DisplayLeakService

public class DisplayLeakService extends AbstractAnalysisResultService {
    public DisplayLeakService() {
    }
    protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {}
}

ServiceHeapDumpListener

public final class ServiceHeapDumpListener implements Listener {
    private final Context context;
    private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;

    public ServiceHeapDumpListener(@NonNull Context context, @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        this.listenerServiceClass = (Class)Preconditions.checkNotNull(listenerServiceClass, "listenerServiceClass");
        this.context = ((Context)Preconditions.checkNotNull(context, "context")).getApplicationContext();
    }

    public void analyze(@NonNull HeapDump heapDump) {
        Preconditions.checkNotNull(heapDump, "heapDump");
        HeapAnalyzerService.runAnalysis(this.context, heapDump, this.listenerServiceClass);
    }
}

4.excludedRefs

执行excludedRefs方法传入了AndroidExcludedRefs.createAppDefaults().build()。AndroidExcludedRefs系统的内存泄漏的默认配置对象。为了把系统的泄漏忽略掉。

//内部类
  public static final class Builder {
        File heapDumpFile; //堆信息文件
        String referenceKey; //key 引用
        String referenceName;//引用的name
        ExcludedRefs excludedRefs;//系统的内存泄漏的引用对象
        long watchDurationMs; //监听  的时间
        long gcDurationMs;// gc的时间
        long heapDumpDurationMs; 
        boolean computeRetainedHeapSize;
        List<Class<? extends Inspector>> reachabilityInspectorClasses;
}

private final Builder heapDumpBuilder = new Builder();

public final T excludedRefs(ExcludedRefs excludedRefs) {
    this.heapDumpBuilder.excludedRefs(excludedRefs);
    return this.self();
}

5.buildAndInstall

返回了RefWatcher,其实就是AndroidRefWatcher对象。此处是建造者模式的build方法,创建对象且install。

 public RefWatcher buildAndInstall() {
        if (LeakCanaryInternals.installedRefWatcher != null) {
            throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
        } else {
        	//此处创建观察者对象
            RefWatcher refWatcher = this.build();
            if (refWatcher != RefWatcher.DISABLED) {
                LeakCanaryInternals.setEnabledAsync(this.context, DisplayLeakActivity.class, true);
                if (this.watchActivities) {
                	//执行activity的观察逻辑
                    ActivityRefWatcher.install(this.context, refWatcher);
                }
				//如果有fragment的配置,也执行fragment的观察逻辑
                if (this.watchFragments) {
                    Helper.install(this.context, refWatcher);
                }
            }

            LeakCanaryInternals.installedRefWatcher = refWatcher;
            return refWatcher;
        }
    }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
####一、前言 在做系统应用的时候,一般都在linus 服务器上面搭建编译环境,编译apk。跟大厂(vivo)合作的时候,验收的最后一关往往是内存泄漏测试,想要保证项目按期验收,我们最好是能在平时开发的时候发现并解决内存泄漏问题,而码环境无法像gradle 一行代码依赖leakcanary,而是需要通过aar或者jar形式添加依赖。刚好前段时间抽空将leakcanary集成到项目中,现在将过程记录下来,为大家踩个坑。 ####二、撸起袖子 老规矩,百度一下... ![image.png](https://upload-images.jianshu.io/upload_images/11562793-8b8621df077a06ea.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 搜到一篇文章,嗯,感觉有我要的东西 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-446a980a54bf60a7.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 点进去一看,额,只是记录了依赖,没给出相应的jar包和arr,版本1.5也不是最新的,所以我还是自己动手吧。 ##### 下载leakcanary码 传送门 https://github.com/square/leakcanary ##### 编译出 aar 选择assembleDebug ![image.png](https://upload-images.jianshu.io/upload_images/11562793-43e389295da509e0.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 生成的aar在目录 /build/outputs/aar 下 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-2360141aa17b6f5e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) leakcanary-android 依赖 leakcanary-analyzer,所以把leakcanary-analyzer-debug.aar也取出来 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-7120eb74d6b5bc0e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) leakcanary-analyzer 依赖 com.squareup.haha:haha:2.0.4 和 leakcanary-watcher 我们把haha:2.0.4的jar包取出来 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-c1dd44406966df29.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 同理把leakcanary-watcher 的jar包取出来。 #### 依赖 整理一下需要依赖的jar包和arr haha-2.0.4.jar leakcanary-analyzer-1.6.2.aar leakcanary-android-1.6.2.aar leakcanary-watcher-1.6.2.jar 把上面的aar和jar放到项目的libs目录下,然后打开Android.mk,开始添加依赖 ``` #依赖申明,冒号前是别名,冒号后是jar包路径 LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := haha:libs/haha-2.0.4.jar \ leakcanary-watcher:libs/leakcanary-watcher-1.6.2.jar \ leakcanary-android:libs/leakcanary-android-1.6.2.aar \ leakcanary-analyzer:libs/leakcanary-analyzer-1.6.2.aar \ include $(BUILD_MULTI_PREBUILT) ...省略其它 #依赖jar包 ...省略其它jar包依赖 LOCAL_STATIC_JAVA_LIBRARIES += haha LOCAL_STATIC_JAVA_LIBRARIES += leakcanary-watcher #依赖aar LOCAL_STATIC_JAVA_AAR_LIBRARIES := leakcanary-analyzer LOCAL_STATIC_JAVA_AAR_LIBRARIES += leakcanary-android ...省略其它 #添加包名 LOCAL_AAPT_FLAGS += --extra-packages com.squareup.leakcanary LOCAL_AAPT_FLAGS += --extra-packages gnu.trove ``` 然后在Application中添加初始化代码 ``` import com.squareup.leakcanary.LeakCanary; #oncreate中 if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); ``` 感觉应该没啥问题,编译一下试试 开始mm 等待中... ####编译失败 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-c905b8d4a55e53c7.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 非法字符 '\$' ?什么鬼,非法字符哪里来的,肯定是LeakCanary 的,看看去。 ![image.png](https://upload-images.jianshu.io/upload_images/11562793-2ae94c9b68676610.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 原来是占位符导致的,这个好办,直接把applicationId 写死不就行了 这里就把 ${applicationId} 换成hello_world,然后编译一下,把aar替换成新编译的,然后mm 等待一分钟... ![image.png](https://upload-images.jianshu.io/upload_images/11562793-b36ea1fbd4f18da8.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 编译成功,install 一下,运行没问题。 那搞个内存泄漏出来看看?![screen.png](https://upload-images.jianshu.io/upload_images/11562793-e92d1a3a1c05738e.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240) 嗯,没毛病,那本次码环境集成LeakCanary工作到此就结束了。 总结: 1.下载LeakCanary 码,修改manifest中的非法字符’\$‘,然后编译,取出需要的aar和jar 2.然后在Android.mk中添加依赖 为何方便大家集成,依赖我已经整理出来了

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值