写了个 Android 性能检测的库,还有人看性能相关的么?

由于工作需要,需要解决一些性能问题,虽然有 Profiler 、Systrace 等工具, 但是无法实时监控,于是计划写一个能实时监控性能的小工具,经过学习大佬们的文章, 最终完成了这个开源的性能实时检测库 。初步能达到预期效果,这里做个记录,算是小结了 。
 
这个性能检测库,可以检测以下问题
  • UI 线程 block 检测
  • App 的 FPS 检测
  • 线程和线程池的创建和启动监控
  • IPC(进程间通讯)监控
同时还实现了以下功能
  • 实时通过 logcat 打印问题
  • 高效保存检测信息到本地
  • 提供上报到指定服务器接口
接入指南1 在 APP 工程目录下面的 build.gradle 添加如下内容
dependencies {debugImplementation "com.xander.performance:perf:0.1.9"releaseImplementation "com.xander.performance:perf-noop:0.1.9"}2 APP 工程的 Application 类新增类似如下初始化代码
private void initPerformanceTool(Context context) {PERF.Builder builder = new PERF.Builder().globalTag("p-tool") // 全局 log 日志 tag ,可以快速过滤日志.checkUI(true, 100) // 检查 ui 线程, 超过指定时间还未结束,会被认为 ui 线程 block.checkThread(true) // 检查线程和线程池的创建.checkFps(true) // 检查 Fps.checkIPC(true) // 检查 IPC 调用.issueSupplier(new PERF.IssueSupplier() {@Overridepublic long maxCacheSize() {// issue 文件缓存的最大空间return 1024 * 1024 * 20;}@Overridepublic File cacheRootDir() {// issue 文件保存的根目录return getApplicationContext().getCacheDir();}@Overridepublic boolean upLoad(File file) {// 上传入口,返回 true 表示上传成功return false;}}).build();PERF.init(builder);}原理介绍
  • UI 线程 block 检测原理
主要参考了 AndroidPerformanceMonitor 库的思路,对 UI 线程的 Looper 里面处理的 Message 过程进行监控 。在 Looper 开始处理 Message 前,在异步线程开启一个延时任务,用于后续收集信息 。如果这个 Message 在指定的 时间段内完成了处理,那么在这个 Message 被处理完后,就取消之前的延时任务,说明 UI 线程没有 block。如果在指定 的时间段内没有完成任务,说明 UI 线程有 block ,在判断发生 block 的同时,我们可以在异步线程执行刚才的延时任务, 如果我们在这个延时任务里面打印 UI 线程的方法调用栈,就可以知道 UI 线程在做什么了 。
但是这个方案有一个缺点,就是无法处理 InputManager 的输入事件,比如 TV 端的遥控按键事件 。通过按键事件的调用方法 链进行分析,最终每个按键事件都调用了 DecorView 类的 dispatchKeyEvent 方法,而非 Looper 的 loop Message 流程 。所以 AndroidPerformanceMonitor 库是无法准确监控 TV 端应用的耗时情况 。针对 TV 端应用按键处理, 需要找到一个新的切入点,这个切入点就是刚刚的 DecorView 类的 dispatchKeyEvent 方法 。那如何介入 DecorView 类的 dispatchKeyEvent 方法呢?我们通过 epic 库来 hook 这个方法的调用,hook 成功后,我们可以在 DecorView 类的 dispatchKeyEvent 方法调用前后都接收到一个回调方法,在 dispatchKeyEvent 方法调用前我们可以在异步线程执行 一个延时任务,在 dispatchKeyEvent 方法调用后,取消这个延时任务 。如果 dispatchKeyEvent 方法耗时时间小于 指定的时间阈值,可以认为没有 block ,此时移除了延时任务 。如果 dispatchKeyEvent 方法耗时时间大于指定的时间阈值 说明此事 UI 线程是有 block 的,此时,就会执行这个延时任务来收集必要的信息 。
以上就是 UI 线程 block 的检测原理了,目前做得还比较粗糙,后续可以考虑参考 AndroidPerformanceMonitor 打印 CPU 、内存等更多的信息 。
最终终端 log 打印效果如下:
com.xander.performace.demo W/demo_Issue: =================================================type: UI BLOCKmsg: UI BLOCKcreate time: 2021-01-13 11:24:41trace:JAVA.lang.Thread.sleep(Thread.java:-2)java.lang.Thread.sleep(Thread.java:442)java.lang.Thread.sleep(Thread.java:358)com.xander.performance.demo.MainActivity.testANR(MainActivity.kt:49)java.lang.reflect.Method.invoke(Method.java:-2)androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:397)android.view.View.performClick(View.java:7496)android.view.View.performClickInternal(View.java:7473)android.view.View.access$3600(View.java:831)android.view.View$PerformClick.run(View.java:28641)android.os.Handler.handleCallback(Handler.java:938)android.os.Handler.dispatchMessage(Handler.java:99)android.os.Looper.loop(Looper.java:236)android.app.ActivityThread.main(ActivityThread.java:7876)java.lang.reflect.Method.invoke(Method.java:-2)com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)复制代码


推荐阅读