Android 渲染(三):掉帧监控

Android 渲染(一):刷新机制

Android 渲染(二):Choreographer、SurfaceFlinger、HWComposer

Android 渲染(三):掉帧监控


WatchDog

  • 开启一个子线程不断向UI线程发送Message,每隔一段时间检查一次刚刚发送的Message是否被处理,如果没有被处理,则说明这段时间主线程被卡住了。
  • 轮询的时间间隔越小,对性能的负面影响就越大,而时间间隔选择的越大,漏报的可能性也就越大。

Looper Printer

  • 替换主线程Looper的Printer,从而监控dispatchMessage的执行时间。(在Android源码中,主线程Looper也会根据执行dispatchMessage的时间来判断是否有卡顿,有则会打印一些日志)
  • 无论是通过反射替换Looper的mLogging还是通过setMessageLogging设置printer,我们只需要替换主线程Looper的printer对象,通过计算执行dispatchMessage方法之后和之前打印字符串的时间的差值,就可以拿到到dispatchMessage方法执行的时间。而大部分的主线程的操作最终都会执行到这个dispatchMessage方法中。
  • 某些类型的卡顿无法被监控到
    • View的TouchEvent中的卡顿无法监控到
      • Touch事件最终是通过server端的InputDispatcher线程传递给Client端的UI线程的,并且使用的是一对Socket进行通讯的
      • 通过PLT Hook,成功hook到libinput.so中的recvfrom和sendto方法,使用我们自己的方法进行替换。当调用到了recvfrom时,说明我们的应用接收到了Touch事件,当调用到了sendto时,说明这个Touch事件已经被成功消费掉了,当两者的时间相差过大时即说明产生了一次Touch事件的卡顿。
    • IdleHandler的queueIdle()回调方法也是无法监控到
      • 反射MessageQueue中的mIdleHandlers,替换为MyArrayList,在我们自定义的MyArrayList中重写add方法,再将我们自定义的MyIdleHandler添加到MyArrayList中。MessageQueue每次执行queueIdle回调方法,都会执行到我们的MyIdleHandler中的的queueIdle方法,就可以在这里监控queueIdle的执行时间了。
    • SyncBarrier(同步屏障)的泄漏同样无法被监控到
      • 不断轮询主线程Looper的MessageQueue的mMessage。SyncBarrier本身也是一种特殊的Message,其特殊在它的target是null。通过反射mMessage,发现当前的Message的target为null,并且通过这个Message的when发现其已经存在很久了,这个时候我们合理怀疑产生了SyncBarrier的泄漏(但还不能完全确定,因为如果当时因为其他原因导致主线程卡死,也可能会导致这种现象),然后再发送一个同步消息和一个异步消息,如果异步消息被处理了,但是同步消息一直无法被处理,这时候就说明产生了SyncBarrier的泄漏。如果激进一些,这个时候我们甚至可以反射调用MessageQueue的removeSyncBarrier方法,手动把这个SyncBarrier移除掉,从而从错误状态中恢复。
      • 无法溯源问题究竟是哪个View导致的。如果发现某个场景下该问题确实较为严重,可以考虑使用插桩或者Java hook在测试环境下debug该问题。
  • BlockCanary 基于 Looper 的性能监控

Choreographer

  • doFrame函数中,Vsync信号来时会标记start_time(即intentedFrameTime),执行doFrame函数时会记录end_time(执行的doFrame的时间),这两个时间差就是 Vsync 处理时延,也就是丢帧
  • doFrame计算都是前一帧的丢帧情况。此度量方式只能记录UI Thread的丢帧,会导致部分丢帧未被统计到。
  • FrameInfo 来负责记录帧的绘制信息,doFrame 执行的时候,会把每一个关键节点的绘制时间记录下来
  • google提供gfxinfo来记录某个app每帧的耗时落在那个区间,这部分内容会记录的文件在JankTracker.cpp文件,当每次queueBuffer()之后,都会调用finishFrame()方法对当前这一帧的耗时进行统计,不过此方式仅支持统计HW Rendering的应用。
  • 丢帧的时间计算方式:FrameCompleted - IntendedVsync
  • 利用Choreographer 提供的FrameCallback中的回调
    • 自定义FrameCallback并实现FrameCallback接口
    • 在合适的位置调用接口
  • 利用FrameInfo进行监控,android 提供gfxinfo可获取丢帧信息
    • adb shell dumpsys gfxinfo packageName framestats
  • 利用android Choreographer类中提供的自身丢帧的计算逻辑
    • 默认SKIPPED_FRAME_WARNING_LIMIT 是30,原则上应用可以hook修改此值来监控应用的场景丢帧情况

SurfaceFlinger

  • 通常我们通过 Systrace 判断应用是否掉帧的时候,一般是直接看 SurfaceFlinger 部分
  • SurfaceFlinger 的主线程在每个 Vsync-SF 的时候是否没有合成
    • 检查发现没有可用的 Buffer 而没有合成操作
    • 被其他的工作占用(比如截图、HWC 等)
    • 在等 presentFence
    • 在等 GPU fence
  • 如果有合成操作,那么需要看 你关心的 App 的 可用 Buffer 个数是否正常:如果 App 此时可用 Buffer 为 0,那么看 App 端为何没有及时 queueBuffer(这就一般是应用自身的问题了),因为 SurfaceFlinger 合成操作触发可能是其他的进程有可用的 Buffer
  • 利用 SurfaceFlinger 进行监控
    • adb shell dumpsys SurfaceFlinger --latency packageName
  • 利用 SurfaceFlinger PageFlip 机制进行监控
    • 使用 : adb service call SurfaceFlinger 1013
    • 备注:需要系统权限
相关推荐
终端安全笔记16 分钟前
配置锁状态那一栏查的是什么:报告里几项各自指向什么风险
android·前端·安全·ios
恋猫de小郭26 分钟前
Kotlin Toolchain 0.13,JetBrains 推荐 KMP 都切换到到全新工具链
android·前端·flutter
安卓机器1 小时前
rom定制系列------小米8 安卓16线刷原生固件 体验新版本
android·小米8安卓16
cyy2981 小时前
小米电视安装tts引擎
android·tv
小锋学长生活大爆炸1 小时前
【工具】在 Android 手机上运行 Docker
android·docker·智能手机
Carson带你学Android1 小时前
OpenAI Dots 登陆手机端:手机上派任务,AI 在云端写代码
android·ios
事圆则缓1 小时前
Android 依赖注入库到底应该怎么选
android
大圣编蚕11 小时前
初探 NDK 的世界:从零开始理解 Android 原生开发
android
云贝贝贝11 小时前
MySQL 慢查询定位与索引优化实战
android
骑着蜗牛撵大象32712 小时前
服务下线不再炸:分层关闭、TCP 存活探测与负载均衡排水的落地套路
android·tcp/ip·负载均衡·tcp·高可用·健康检查·优雅关闭