设为首页 加入收藏

TOP

想让安卓app不再卡顿?看这篇文章就够了(一)
2019-09-01 23:14:17 】 浏览:73
Tags:安卓 app 不再 卡顿 篇文章

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由likunhuang发表于云+社区专栏

实现背景

应用的使用流畅度,是衡量用户体验的重要标准之一。Android 由于机型配置和系统的不同,项目复杂App场景丰富,代码多人参与迭代历史较久,代码可能会存在很多UI线程耗时的操作,实际测试时候也会偶尔发现某些业务场景发生卡顿的现象,用户也经常反馈和投诉App使用遇到卡顿。因此,我们越来越关注和提升用户体验的流畅度问题。

已有方案

在这之前,我们将反馈的常见卡顿场景,或测试过程中常见的测试场景使用UI自动化来重复操作,用adb系统工具观察App的卡顿数据情况,试图重现场景来定位问题。

常用的方式是使用adb SurfaceFlinger服务和adb gfxinfo功能,在自动化操作app的过程中,使用adb获取数据来监控app的流畅情况,发现出现出现卡顿的时间段,寻找出现卡顿的场景和操作。

方式1:adb shell dumpsys SurfaceFlinger

使用‘adb shell dumpsys SurfaceFlinger’命令即可获取最近127帧的数据,通过定期执行adb命令,获取帧数来计算出帧率FPS。

优点:命令简单,获取方便,动态页面下数据直观显示页面的流畅度;

缺点:对于静态页面,无法感知它的卡顿情况。使用FPS在静态页面情况下,由于获取数据不变,计算结果为0,无法有效地衡量静态页面卡顿程度;

通过外部adb命令取得的数据信息衡量app页面卡顿情况的同时,app层面无法在运行时判断是否卡顿,也就无法记录下当时运行状态和现场信息。

方式2:adb shell dumpsys gfxinfo

使用‘adb shell dumpsys gfxinfo’命令即可获取最新128帧的绘制信息,详细包括每一帧绘制的Draw,Process,Execute三个过程的耗时,如果这三个时间总和超过16.6ms即认为是发生了卡顿。

优点:命令简单,获取方便,不仅可以计算帧率,还可以观察卡顿时每一帧的瓶颈处于哪个维度(onDraw,onProcess,onExecute);

缺点:同方式1拥有一样的缺点,无法衡量静态页面下的卡顿程度;app层面依然无法在发生卡顿时获取运行状态和信息,导致跟进和重现困难。

已有的两种方案比较适合衡量回归卡顿问题的修复效果和判断某些特定场景下是否有卡顿情况,然而,这样的方式有几个明显的不足:

1、一般很难构造实际用户卡顿的环境来重现;

2、这种方式操作起来比较麻烦,需编写自动化用例,无法覆盖大量的可疑场景,测试重现耗时耗人力;

3、无法衡量静态页面的卡顿情况;

4、出现卡顿的时候app无法及时获取运行状态和信息,开发定位困难。

全新方案

基于这样的痛点,我们希望能使用一套有效的检测机制,能够覆盖各种可能出现的卡顿场景,一旦发生卡顿,能帮助我们更方便地定位耗时卡顿发生的地方,记录下具体的信息和堆栈,直接从代码程度给到开发定位卡顿问题。我们设想的Android卡顿监控系统需要达到几项基本功能:

1、如何有效地监控到App发生卡顿,同时在发生卡顿时正确记录app的状态,如堆栈信息,CPU占用,内存占用,IO使用情况等等;

2、统计到的卡顿信息上报到监控平台,需要处理分析分类上报内容,并通过平台Web直观简便地展示,供开发跟进处理。

如何从App层面监控卡顿?

我们的思路是,一般主线程过多的UI绘制、大量的IO操作或是大量的计算操作占用CPU,导致App界面卡顿。只要我们能在发生卡顿的时候,捕捉到主线程的堆栈信息和系统的资源使用信息,即可准确分析卡顿发生在什么函数,资源占用情况如何。那么问题就是如何有效检测Android主线程的卡顿发生,目前业界两种主流有效的app监控方式如下,在《Android卡顿监控方式实现》这篇文章中我将分别详细阐述这两者的特点和实现。

1、利用UI线程的Looper打印的日志匹配;

2、使用Choreographer.FrameCallback

方式3: 利用UI线程的Looper打印的日志匹配判断是否卡顿

Android主线程更新UI。如果界面1秒钟刷新少于60次,即FPS小于60,用户就会产生卡顿感觉。简单来说,Android使用消息机制进行UI更新,UI线程有个Looper,在其loop方法中会不断取出message,调用其绑定的Handler在UI线程执行。如果在handler的dispatchMesaage方法里有耗时操作,就会发生卡顿。

img

只要检测msg.target.dispatchMessage(msg) 的执行时间,就能检测到部分UI线程是否有耗时的操作,从而判断是否发生了卡顿,并打印UI线程的堆栈信息。

优点:用户使用app或者测试过程中都能从app层面来监控卡顿情况,一旦出现卡顿能记录app状态和信息, 只要dispatchMesaage执行耗时过大都会记录下来,不再有前面两种adb方式面临的问题与不足。

缺点:需另开子线程获取堆栈信息,会消耗少量系统资源。

方式4: 利用Choreographer.FrameCallback监控卡顿

我们知道, Android系统每隔16ms发出VSYNC信号,来通知界面进行重绘、渲染,每一次同步的周期为16.6ms,代表一帧的刷新频率。SDK中包含了一个相关类,以及相关回调。理论上来说两次回调的时间周期应该在16ms,如果超过了16ms我们则认为发生了卡顿,利用两次回调间的时间周期来判断是否发生卡顿(这个方案是Android 4.1 API 16以上才支持)。

这个方案的原理主要是通过Choreographer类设置它的FrameCallback函数,当每一帧被渲染时会触发回调FrameCallback, FrameCallback回调void doFrame (long frameTimeNanos)函数。一次界面渲染会回调doFrame方法,如果两次doFrame之间的间隔大于16.6ms说明发生了卡顿。

img

优点:不仅可用来从app层面来监控卡顿,同时可以实时计算帧率和掉帧数,实时监测App页面的帧率数据,一旦发现帧率过低,可自动保存现场堆栈信息。

缺点:需另开子线程获取堆栈信息,会消耗少量系统资源。

总结下上述四种方案的对比情况:

SurfaceFlinger gfxinfo Looper.loop Choreographer.FrameCallback
监控是否卡顿
支持静态页面卡顿检测 × ×
支持计算帧率 ×
支持获取App运行信息 × ×

实际项目使用中,我们一开始两种监控方式都用上,上报的两种方式收集到的卡顿信息我们分开处理,发现卡顿的监控效果基本相当。同一个卡顿发生时,两种监控方式都能记录下来。 由于Choreographer.FrameCallback的监控方式不仅用来监控卡顿,也方便用来计算实时帧率,因此我们现在只使用Choreographer.FrameCallback来监控app卡顿情况。

痛点1:如何保证捕获卡顿堆栈的准确性?

细心的同学可以发现,我们通过上述两

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇SVN上传的时候没法显示文件名,只.. 下一篇adb连接安卓模拟器

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目