打造秒级异常监控工具

问题背景

在一个迭代开发完毕之后,ci构建好测试包,交给测试人员进行测试,随后在测试的过程中,出现了一些问题,有些很容易追踪,比如一些逻辑bug,需求没有实现,但还是有一些需要花费一些经历去排查,比如:

1、app crash 了,crash然因是什么,也许你等几分钟才能在rdm或者bugly上看到,而且还不能直观看到具体是按个异常导致这次crash,总之你并不能根据描述及时推断,而且可能比较诡异的事情是,按照测试同学反馈给你的路径操作,你并没有发现,那么,我们不妨思考下,出现这个问题的原因是什么?(可以先思考,在看下面的分析)

2、测试说,某个功能模块在他手机上感觉会比较卡,然而比较诡异的事情是,在你的测试机上似乎也并不卡呢。

好,我们开始思考,在开发app的过程中,起初我们刚刚起步的时候,追求的是能够使用各种轮子快速实现需求,渐渐的,我们的段位提升了,也有了新的追求,比如注重代码质量,如何写更好维护的代码,比如注重框架的扩展性,能够更优雅的融入更加多的功能模块,插件化支持等等,然而,我们可能往往会忽视性能及质量的问题,因为这个问题确实并非那么显而易见。

分析问题

前面提到了app crash了,然而情况比较诡异,你复现不了,可能有的然因我简单的总结下(显然不仅仅是我总结的这些原因,大千世界无奇不有):

1、账号不具备出发这个crash,可能你的账号是老账号,各种条件都符合,而他是一个刚注册不久的账号,这点你可能需要几分钟反应过来,或许一上午也反应不过来。

2、兼容性问题,某些手机厂商自己定制了一些特性导致,可能过段时间,你去RDM或者bugly上看bug都是某些机型。

3、OOM,这个我们并不模式,手机能力各有千秋,你手机不崩溃自然他本事好,不过可以肯定你代码有问题了,得治疗啊。

4、ANR,嗯,和这上面那个也挺像的,不用过多解释。

5、你自己的包好好的,给测试的包就跪了,可能你很久才意识到这个是混淆的问题。

总而言之,对于这些问题,你自然而然不是那么机警,已遇到就定位到了,然后就愉快的去改改改!或多或少的都会耗费你时间,我们不妨想一想,你遇到这些问题,是怎么解决的?

1、找测试同事借手机测试?假如测试人不在你身边呢?

2、去weTest上找相关的机型进行测试,嗯,总算复现了,但是这个过程体验并不轻松啊。

你也许在想,还是在链接as开发的时候爽,crash了,logcat中自然就有崩溃的信息,鼠标一点就过去了,三下五除二,分分钟灭bug于无形,但是条件艰苦,我们该怎么办?

有没有一种办法,在他们测试app的时候,一旦crash,就把crash的堆栈,内存信息(是否oom),机型(防止兼容性),帧率(fps),cpu利用率,页面访问路径等信息拿到交给我们,嗯,顺着这个思路,看看我们如何做。

打造工具

注意,我们的切入点是在app发生crash的时候,那么,我们如何得到这个切入点,很容易想到的是,如何捕捉这个异常,Google上略施小计,就可以找到一堆文字来探讨这方面的问题,如这篇文章云云。

1、crash捕捉

显而易见,它自定义的异常处理handler先处理一下异常,最后在交给默认异常处理handler。

2、内存信息获取

拿到机器的可用内存信息可以参考:

ActivityManager activityManager = (ActivityManager) application.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();

 拿到app目前的内存状况可以参考:

Debug.MemoryInfo debugMemInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(debugMemInfo);

 这里问一个问题,应用可能分配多少内存,这个从哪里取呢?(activityManager.getMemoryClass())

3、机型数据

这个很好办,就在Build信息中 ,如Build.MANUFACTURER,制造商,嘿嘿。其他不一一举例。

4、帧率/cpu率用率

嗯,这个,不止可否这样:

HardwarePropertiesManager propertiesManager = (HardwarePropertiesManager)context.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE);
CpuUsageInfo[] cpuUsages = propertiesManager.getCpuUsages();

 答案是不行,这个只有系统api才有权限访问。

嗯,那么这样呢?

cpuReader = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/stat")), BUFFER_SIZE);
String cpuRate = cpuReader.readLine();

 然后在来分析,答案是,不行,Android O之后,会出现java.io.FileNotFoundException: /proc/stat (Permission denied)

嗯,但是这个是/proc/pid/stat 可以的,也就是说,应用取自己本身的没问题,但是这个数据似乎没啥作用,作罢,取不到cpu率用率,取帧率是可以的吧?

嗯,利用Choreographer.FrameCallback,轻松搞定

5、页面访问路径

这个就更加愉快了,registerActivityLifecycleCallbacks就可以搞定。

好,经过上面的分析,似乎拿到这些信心问题不大,那么就剩下继承在一起了而已。不过我们最好是观察下,是否有人做了这些,运气挺好的,搜索到一个,只是个人感觉并不完善,所以还不能直接投入使用,封装之后,大家是可以直接使用的。

implementation 'com.tencent.tip:exception-toast:1.1.7-SNAPSHOT'

在application onCreate中加入代码,注意其中生成环境的设置。 

new ExceptionHandler.Builder(this).addExceptionInterceptor(new ExceptionHandler.ExceptionInterceptor() {
            @Override
            public void onException(ExceptionInfoBean exceptionInfoBean) {
                //这里可以上传错误信息到后台
                Log.e("Application", "onException() called with: exceptionInfoBean = [" + exceptionInfoBean + "]");
            }
        }).setTrackActivitiesEnabled(true)
                .setIsProductionMode(isRelease())
                .setmFpsUpdateTime(1000)
                .build();

 效果展示

总结

其中,我们预留的接口onException中的对象包含了收集到的所有信息,因为在生产环境的时候,我们不能崩溃了在弹一个界面出来吧,因此,这里可以将信息传递给我们的管理端来管理异常信息。

优势:这些信息对比bugly或者rmd上来看的话,具备他们不具备的一些信息,不如,内存信息,帧率,页面访问路径等,因此,定位问题可能比rdm等更加方便。

还存在问题。

1、目前管理端还在建设当中。

2、存在混淆的代码出现的异常在app上也不大好看,解决的办法是读取符号表,还原。

参考资料

https://github.com/TencentOpen/GT

https://github.com/markzhai/AndroidPerformanceMonitor

https://issuetracker.google.com/issues/37140047

https://www.makeuseof.com/tag/get-logcat-reporting-bugs-android/

https://android.jlelse.eu/handling-uncaught-exceptions-in-android-d818ffb20181

https://github.com/RohitSurwase/UCE-Handler

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区