Android Memory Leak检测上报

背景

之前统计crash信息时统计到的top5的崩溃,OOM导致的崩溃数量排在第二位

Android Memory Leak检测上报_第1张图片
crash top5分布

Android sdk的OOM崩溃率持续增长,为了检测出内存泄漏问题,决定改造下LeakCanary解决目前测试中的几个痛点。

  1. Memory leak发生时不能很快的将信息传递给开发人员,之前需要打开leak activity 然后找到发生的leak记录,打开每级的stack信息。截图给开发,简直不能忍。

    1. 原始memory leak发生时的通知提示,打开以后如下图,需要将这个图给到开发定位问题。
    Android Memory Leak检测上报_第2张图片
    image
  2. 当测试需要清除应用数据时会造成leak信息一并被清除,导致数据丢失,leak信息无法追溯。

  3. 当使用monkey等自动测试手段进行稳定性测试时,查看leak trace的界面是一个新的Activity,在跑Monkey的过程中会进入这个activity不断的操作,会删除已经产生的leak信息,并且会有相当长一段时间可能无法回到测试产品自身的界面中操作。

Leakcanary的使用

  • 原理图

Android Memory Leak检测上报_第3张图片
image
  • 过程解析

    • RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。

    • 然后在后台线程检查引用是否被清除,如果没有,调用GC。

    • 如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。

    • 在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。

    • 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄漏。

    • HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄漏。如果是的话,建立导致泄漏的引用链。

    • 引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

一、开始使用

在项目 build.gradle 中加入引用,不同的编译使用不同的引用:

 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
 }

在 Application 中:

public class MainApplication extends Application {
 
  @Override 
    public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
 }
}

这样,在 debug build 中,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。点击通知即可进入leak activity进行查看相关的leak详情。

二、改造

1、新建LeakUploadService

在Application类所在的package内新建一个LeakUploadService类,继承DisplayLeakService类:

import com.squareup.leakcanary.*
public class LeakUploadService extends DisplayLeakService {
    @Override
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
        if (!result.leakFound || result.excludedLeak){
            return;
       }
  uploadService();
   }
}

其中,发生泄漏的类名为result.className.toString();,其余信息诸如软件包名、软件版本号、leak trace等,均在leakInfo中一般将软件包名、版本号、leak trace(第一行下面,* Retaining: 131 KB.之上的部分)、泄漏大小等信息上传到数据库即可,有了这些信息,开发就可以定位问题了。

处理方法也很简单,就是对String对象进行一些操作。这里我是将发送泄漏的类名、软件包名、软件版本号、整个泄漏信息(除了Details部分)上传到数据库

String className = result.className.toString();
String pkgName = leakInfo.trim().split(":")[0].split(" ")[1]
String pkgVer = leakInfo.trim().split(":")[1]
String leakDetail = leakInfo.split("\n\n")[0] + "\n\n" + leakInfo.split("\n\n")[1];

使用Okhttp编写一个简单的上传leak信息的代码,在LeakUploadService中检测到memoryload时调用上传即可。

2、注册service

接下来需要在AndroidManifest.xml中注册service,即在之间添加,根据LeakUploadService所在的包自行调整。

3、改造屏蔽leak activity

DisplayLeakActivity类是LeakCanary展示leak trace的类,这个类的存在,会导致跑Monkey的过程中,多次进入这个Activity,在其中操作,从而减少在软件本身界面中的操作时间。

屏蔽DisplayLeakActivity

  • 由于LeakCanary类正好是在前面接入LeakCanary时添加代码LeakCanary.install(this);用到的类,因此想到的方法是自己创建一个新的类LeakCanaryWithoutDisplay,位置在com/squareup/leakcanary下(需要自己新建这个package),里面的代码直接复制LeakCanary类,然后做如下修改:

    • public final class LeakCanary {改为public final class LeakCanaryWithoutDisplay {

    • private LeakCanary() {改为private LeakCanaryWithoutDisplay() {

    • 修改enableDisplayLeakActivity()函数,将true改为false

    • 将主Application类中安装LeakCanary的代码LeakCanary.install();改为LeakCanaryWithoutDisplay.install();

    • 调用LeakCanaryWithoutDisplay.enableDisplayLeakActivity(this);之后就会屏蔽activity和通知信息。

  • 最后修改主Application类中的安装方式,改为:

public class MainApplication extends Application {
    private RefWatcher refWatcher;
    @Override
    public void onCreate() {
        super.onCreate();
  refWatcher = LeakCanaryWithoutDisplay.install(this);
  LeakCanaryWithoutDisplay.enableDisplayLeakActivity(this);
   }
}

做了如上操作后,每次发生泄漏都不会出现通知和leak activity,并且会自动将发送泄漏的类名、软件包名、软件版本号、泄漏信息等上传指定服务端了。

4、 服务端简单实现

  1. 主要功能实现
  • 接受LeakUploadService post过来的leak数据

  • 对数据处理并入库

  • 定时任务触发,通过邮件的形式上报收集到的leak信息,并更新数据库状态

  • 重复leak的过滤

2 重复leak信息过滤,主要依据infer生成的report json对比实现,当前可以完全过滤掉重复的leak信息。
通过对比发现生成的数据中,可以通过下面的代码过滤重复信息


Android Memory Leak检测上报_第4张图片
image2019-4-12_15-28-43.png

三、 测试

测试中无需关注leak的发生,只需要关注被测应用的功能测试,当leak发生时会自动上传leak信息到服务端,服务端会自动邮件通知开发者。

四、改造以后于原始方式的对比

  1. 改造以后可以通过邮件直接通知开发,memoryleak的发生,还有泄露的详细信息,

  2. 测试过程中完全可以不再关注leak的发生,发生leak也不会再中断测试过程。

  3. 邮件通知,减少测试与开发之间沟通的成本

  4. 提升测试工作效率,减少不必要的时间开支

你可能感兴趣的:(Android Memory Leak检测上报)