一直以来Android性能测试一直是Android测试中一个被一部分人遗忘,有被一部分人无可奈何的东西。在绝大部分的创业公司,性能测试基本上都是被遗忘的,因为功能测试和稳定性测试才是重点,而在中等公司中一部分测试人员向对Android进行性能测试,却无从下手。Android性能测试一直存在测试维度少,测试数据难收集,已收集数据难量化的特点,这些特点又是因为Android手机版本碎片化、硬件多样化、App功能复杂造成的。


性能测试总的来说,可以分为卡顿ANR测试、流畅度测试、电量测试、流量测试。一个APP为什么需要性能测试,总的来说就是一些不严谨的代码,在低端机型造成卡顿,对手机上有限电量的浪费,昂贵流量的浪费,造成用户流失。


一、卡顿ANR测试


卡顿ANR与Android就是天生的朋友,从Android第一天诞生直到现在的8核CPU,Android还是未能摆脱页面不流畅,卡,死机的诟病,所以个人认为卡顿ANR测试是性能测试最主要的一块。


卡顿简单的来说,就是手机没有及时响应、页面延迟,出现丢帧的现象,或者点击无响应。绝大多数的卡顿,稍等片刻系统就会恢复正常,但假如超过5S,就可能会引发手机ANR,造成更高级别的警告。如图所示:


1.1 什么会引发ANR?


在Android里,应用程序的响应性是由Activity Manager和WindowManager系统服务监视的 。当它监测到以下情况中的一个时,Android就会针对特定的应用程序显示ANR:

ANR一般有三种类型:


1)KeyDispatchTimeout(5 seconds) --主要类型按键或触摸事件在特定时间内无响应

2)BroadcastTimeout(10 seconds) --BroadcastReceiver在特定时间内无法处理完成

3)ServiceTimeout(20 seconds) --小概率类型 Service在特定的时间内无法处理完成


这三种原因都会造成ANR,但是第一种情况基本上占了所有ANR的百分之九十以上,第三种的情况微乎其微。这三种ANR不是孤立的,有可能会相互影响。例如一个应用程序进程中同时有一个正在显示的Activity和一个正在处理消息的BroadcastReceiver,它们都运行在这个进程的主线程中。如果BR的onReceive函数没有返回,此时用户点击屏幕,而onReceive超过5秒仍然没有返回,主线程无法处理用户输入事件,就会引起第1种ANR。如果继续超过10秒没有返回,又会引起第2种ANR。发生ANR的主要原因是潜在的耗时操作,例如网络或数据库操作,或者高耗时的计算如改变位图尺寸,应该在子线程里(或者以数据库操作为例,通过异步请求的方式)来完成。然而,不是说你的主线程阻塞在那里等待子线程的完成——也不是调用 Thread.wait()或是Thread.sleep()。替代的方法是,主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程。以这种方式设计你的应用程序,将能保证你的主线程保持对输入的响应性并能避免由于5秒输入事件的超时引发的ANR对话框。


三种ANR发生时都会在log中输出错误信息,你会发现各个应用进程和系统进程的函数堆栈信息都输出到了一个/data/anr/traces.txt的文件中,ROOT手机导出该文件后,分析此日志可以得出一些结论,但traces文件信息比较抽象,难理解。总的来说,日志难收集,结果难分析。


1.2 如何避免ANR?


1) 运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。(可以采用重新开启子线程的方式,然后使用Handler+Message的方式做一些操作,比如更新主线程中的ui等)


2) 应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为 BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个 Service。(此处需要注意的是可以在广播接受者中启动Service,但是却不可以在Service中启动broadcasereciver,关于原因后续会有介绍,此处不是本文重点)


3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。


TraceView是android SDK中自带的一个性能测试工具,可以在Tools目录下找到。TraceView能很精确的查看到每一个类,每一个类方法的执行时间。APP卡顿我们只需要保留当时的traceview文件,通过查看该文件,就可以定位绝大部分问题。


通过Debug.startMethodTracing(String FileName)和Debug.stopMethodTracing()来记录一段时间内方法执行情况。


在主线程中不停的插入一个轻量级别的操作,如果该变量在指定的时间内,没有改变,则说明此刻APP卡顿。卡顿工具工具就是根据https://github.com/SalomonBrys/ANR-WatchDog项目改进而成的。把ANR这种警告变成错误让APP闪退,持久化当时信息。


原理图:

问题背景:


百度国际化浏览器初次安装App,点击icon后,明显卡顿或者ANR,QA手工测试无法定位,RD优化代码多次依旧找不到问题的节点。


测试方法:

百度国际化浏览器加入卡顿工具jar,代码中初始化。测试结果分析。


在打开traceview 文件后,通过 real Time/Call 从大到小排序,找到对应的与代码相关消耗时间最大的方法。

Android性能测试_第1张图片

我们能够看到很明显的看出FrameWindow.initDataBase()方法占用CPU过长达到3S左右了,距离5S很接近,通过查看代码,结合业务逻辑,得知此处为数据库初始化,并且主要是标签数据库初始化。


从整个APP来看,启动页面初始化标签数据库并没有错,但是此刻本身逻辑就非常多,标签数据库初始化后并没有马上使用到,而是到二级页面才有查询动作,总的来说,就是增加资源紧张。


建议:标签数据库什么时候使用,什么时候初始化。建议放到二级页面初始化,减少页面App页面启动的负荷,减少冷启动时间,避免卡顿和ANR。并且标签数据库初始化放在线程中。


二、流畅度测试


流畅度测试简单的来说就是Android页面绘制。Android系统每秒60hz,也就是大约每16ms刷新一次界面。但是在我们使用APP过程中,经常会看到页面有卡顿,或者说丢帧的现象。也就是说可能此刻两个页面绘制的时间差超过0.1S(人眼视觉残留0.1S)。总的来说,就是页面


原理分析


在确定衡量指标之前,我们先来研究一下Android的UI更新机制。


2.1 Android如何绘制UI?=


关于Android是如何更新UI,相信已经有很多文章介绍其中的步骤以及过程,大体上可以用下图来展示:


Android性能测试_第2张图片


从图中可以看到无论那条路走下去始终都由SurfaceFlinger来控制最后的更新。


在Android版本更新过程中,发现在Jelly Bean中Google加入了一个Project Butter,用来解决严重影响Android口碑的问题之一“UI流畅性差”的问题。而Project Butter中主要引入了三个核心元素:VSYNC(垂直同步)、Triple Buffer和Choreographer。


2.2从VSYNC开始


VSync是Vertical Synchronization(垂直同步)的缩写,是一种在PC上很早就广泛使用的技术,可以简单的把它认为是一种定时中断。而在Android 4.1(JB)中已经开始引入VSync机制。


上图所示是VSync机制下的绘制过程。从上图可以看出,CPU和GPU的处理时间都少于一个VSync的间隔,即16.6ms。如果每个间隔都有绘制的情况下,当前的FPS即为60帧。


当CPU和GPU处理时间都很慢,或因为其他的原因,如在主线程中干活太多,那么就会出现如下图这样的状况。

从上图可以看到,CPU和GPU的处理时间因为各种原因都大于一个VSync的间隔(16.6ms),所以在第二个VSync还在处理1区域的绘制时,不可能实现理论上的FPS60,同时也出现了丢帧(SF: Skipped Frame)情况。


为了便于理解,上图用的是双Buffer机制的情况,实际上Android 4.1引入了Triple Buffer,所以当双Buffer不够用时,Triple Buffer丢帧的情况如下图所示。


VSync机制就像是播放动画片(60帧/s)。每次都会播放画面,有的时候有人偷懒了,机器坏了,就会出现播放速度降低的状况。我们把这个播放速度叫做流畅度。


2.3 从FPS&丢帧到流畅度(SM: SMoothness)


实际上在很多Android的App中,很少有需要不断地去绘制的场景,很多时候页面都是静态的。也就是会出现这样的状况,虽然1s中VSync的60个Loop不是每个都在做绘制的工作,FPS会比较低,但并不代表这个时候程序不流畅(如我将App放着不动,实测FPS为1)。所以FPS较低并不能代表当前App在UI上界面不流畅,而1s内VSync这个Loop运行了多少次更加能说明当前App的流畅程度。所以,下面这2个指标比FPS更能代表当前的App是否处于流畅的状态。同样这2个指标更加能够量化App卡顿的程度:


1)丢帧(SF: Skipped Frame):如上图2所示情况应该在16.6ms完成工作却因各种原因没做完,占了后n个16.6ms的时间,相当于丢了n帧。


2)流畅度(SM: SMoothness):和丢帧相对,在VSync机制中1s内Loop运行的次数。


和丢帧相对1s内有60个Loop因为某几次工作时间超过了16.6ms(丢帧),这样Loop就无法运行60次(理论最大值)。


当流畅度越小的时候说明当前程序越卡顿。


2.4数数:如何得到流畅度(SM: SMoothness)


接着上面的结论,如果在这样的机制下每次Loop运行之前进行通知,记个数就好了。


很幸运我们在新的Android的那一套机制中找到了一个画图的打杂工Choreographer这个对象。根据Google的官方API文档描述中,它是用来协调animations、input以及drawing时序的,并且每个Loop共用一个Choreographer对象。


下图为Choreographer的定义和结构。

Android性能测试_第3张图片


结论


通过如上原理分析可以得出结论:


1) Android 4.1引入了VSync机制后,可以通过其Loop来了解当前App最高绘制能力。


固定每隔16.6ms执行一次(这个值是一个静态变量,会根据系统版本不同而采用不同的值,目前测试版本是16.6ms这样最高的刷新的帧率就控制在60FPS以内);


如果没有以上事件的时候同样也会运行这样一个Loop;


这个Loop在1s之内运行了多少次,即可以表示当前App绘制的最高的能力,也就是Android App卡顿的程度;


另外,在一次Loop时如果执行时间超过了16.6ms,那么用多于16.6ms的时间除以16.6ms,即是当前App的丢帧(SF: Skipped Frame)。


2)可以在Choreographer的回调FrameCallback中,按秒计数表示当前App的流畅程度,即流畅度SM(SMoothness)。


采用这样方式就可以在App内部观测当前App的流畅度了。并且在丢帧的地方打印traceView,就可以知道丢帧的大概原因,大概位置。定位代码问题。


三、 SmartMonkey测试


Android自动化测试中,monkey测试是一种传统的稳定性测试工具。它可以随机产生事件,不带任何主观性,并且使用方便。但是,正是由于这种随机性,使得传统的monkey测试只能作为稳定性测试工具,在其上进行功能扩展较为不易。在monkey测试中,由于事件的随机性,使得monkey容易卡在某些简单页面,比如登陆页面这种可操作内容很少的页面。


3.1 针对这些问题,我们基于Robotium自动测试框架,开发了SmartMonkey工具。它具有以下这些特点:


准确识别页面上的操作,避免无效点击

支持关键路径配置,使测试范围可控

操作优先级动态变化,覆盖更多功能和页面

多进程基础性能信息自动采集

结合性能专项工具,进一步挖掘性能隐患

支持Checklist配置,提供简单的功能验证

3.2如何使用


SmartMonkey工具本质上是一个大型的case,其使用方法也与普通的case执行方法相似。

建立一个Android Test Project工程

修改AndroidManifest.xml文件

修改instrumentation TAG中的name和targetPackage字段内容如下。

Android性能测试_第4张图片


3.3导入SmartMonkey所需的lib


Android性能测试_第5张图片


3.4在测试工程的src文件中,新建JUnit Test Case该类需继承com.baidu.lynq.lynq.LynQBaseCase类。


Android性能测试_第6张图片


3.5 在新建的Case中添加以下code


其中LAUNCHER_ACTIVITY_FULL_CLASSNAME为被测APP的launcher activity,TARGET_PACKAGE为被测包的包名。

privatestaticfinalStringLAUNCHER_ACTIVITY_FULL_CLASSNAME="com.example.android.apis.ApiDemos";

privatestaticfinalStringTARGET_PACKAGE="com.example.android.apis";

publicTest()throws ClassNotFoundException{

super(TARGET_PACKAGE, Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME));

}

/*

* Smart monkey示例

*/

publicvoidtest_sample()throws ClassNotFoundException {

mSolo.sleep(10000);

mSolo.analysis();

}

3.6添加配置项


在项目中新建assets文件夹,添加AdvancedConfig.properties和config.properties文件


在config.properties文件中添加配置项:

# report path. MUST BE absolute path.

mReportPath =/mnt/sdcard/lynq-report

# login switch. value:{true, false}

# true -> automatic login when ENTER the LOGIN PAGE.

# login page MUST be baidupass. (http://wappass.baidu.com/passport/login)

mLoginSwitch =true

#username. using in login function

mUsername =USERNAME

# password. using in login function

mPassword =PASSWORD


其中mReportPath是设置输出报告的位置, 默认为/mnt/sdcard/lynq-report/目录,mLoginSwitch是自动登录开关,当前仅限baidu passport SDK自动登录。mUsername和mPassword分别为自动登录时输入的用户名和密码。


在AdvancedConfig.properties文件中添加配置项:

# time slot of performance collector. unit:ms. value:int

mTimeSlot =10000

# This is performance report type.value:int{0,1}defualtis 0,report ishtml file. when valueequels1,the report isxmlfile.

mPerformanceReportType =0

# sleep time. interval between two action. unit:ms. value:int

mSleepTime =1000

# default text foreditview. value: string

# NEVER enter any: commenting thisconfigitem OR make it empty

mDefaultText =defaulttextINsmartmonkey

# text for special EditText. split by "|", format: viewID1,content1|viewID2,content2

# viewID: EditText's id, content: text for this EditText

mSpInputText =1234,specialtextforeditTextwithid1234|123,specialtextforeditTextwithid123

# view never click. split by "|"

mNeverClick =\u9000\u51FA\u767B\u5F55|button_settings_logout

# view must click. split by "|"

mMustClick =positiveButton|\u53D6\u6D88

# max operation running time. working in mode1 && mode2.

# value: String

mMaxRunningTime =08:00:00

其中,mTimeSlot是性能数据采集的间隔,mPerformanceReportType是设置性能报告输出样式,1为xml样式,0为html样式。


mSleepTime是两次事件的执行间隔,mDefaultText是默认的SmartMonkey在EditText中输入的内容。mSpInputText中可以配置在某些输入框中特殊输入的内容用竖线分割,比如,”1234,specialtextforeditTextwithid1234”,表示在id为1234的view中,输入“specialtextforeditTextwithid1234”内容。


mNeverClick和mMustClick用来表示关键路径,分别为避免点击的view和必点的view。View可以用文字或十进制id或id string来表示。例如下图中右下角OK按钮,在R文件中是“publicstaticfinalintbtn_ok=0x7f09001d; ”,所以,这个view可以用其上文字“OK”来表示,也可以用id string “btn_ok”或十进制id“2131296285”来表示。


mMaxRunningTime是SmartMonkey的最大执行时间,用时分秒来表示。



3.7执行


通过Run as Android Junit Test方式执行。

四、查看输出报告


4.1 crash信息


SmartMonkey会自动记录被测APP的crash栈信息,以及native crash信息。


Crash信息会输出在你配置的目录中,以stack为开头的txt文件。每个crash单独输出一个文件。Native crash信息记录在以dmp开头的文件中,可以通过google-breakpad进行查看。


4.2基础性能报告


根据配置项,SmartMonkey会输出性能报告到输出报告目录中。性能报告是以performance开头的html或xml文件


Html格式的性能报告中,首先会列出被测app的相关信息,包括包名、uid和同uid下的每一个进程的pid和进程名等。随后列出CPU、内存、流量的图表。CPU图表中记录了每一个进程的CPU占用率,内存图表中记录了每个进程PSS和USS的占用情况,流量图表中记录了流量总使用情况和两个采集点之间的流量差值。


在每个图表上,用node记录了这个节点上SmartMonkey执行的事件,可以用来辅助定位造成曲线波动的操作。

Android性能测试_第7张图片

Android性能测试_第8张图片

Xml格式的性能报告中,每个operation为一个采集点,其中记录了时间戳、测试手机总CPU占用率、流量差值、流量总和、节点上的事件,以及每个进程的pid、CPU占用率、PSS、USS等。

Android性能测试_第9张图片


老罗视频第二季重磅来袭:OkHttp网络框架讲解 老罗 OkHttp网络框架讲解链接:http://pan.baidu.com/s/1bEqnsa
老罗视频第二季重磅来袭:Picasso图片缓存框架讲解
老罗Picasso图片缓存框架讲解链接: 链接:http://pan.baidu.com/s/1eR3YEJo
老罗视频第二季重磅来袭:ORMLite数据库框架讲解 老罗ORMLite数据库框架讲解链接:链接:http://pan.baidu.com/s/1nvhac2l
老罗视频第二季重磅来袭:GreenDao数据库框架讲解 老罗GreenDao数据库框架讲解链接:链接:http://pan.baidu.com/s/1nvRbncd
扫一扫关注下面二维码回复“老罗”获取下载密码


或者在微信中搜索微信公众号ID:appjiagou



原文链接:
http://neast.cn/forum.php?mod=viewthread&tid=61555&fromuid=5


更多相关文章

  1. [笔记] android/iOS自动化测试神器Appium小结
  2. android中java与js通信(可以用html来做页面,进行交互)
  3. Android页面去掉标题全屏的方法-第一次用安卓试试看
  4. android 项目中接入flutter模块(部分页面)
  5. 通过AndroidTestCase来进行android 单元测试
  6. android应用程序跳转到系统的各个设置页面
  7. Android 自动化测试框架Instrumentation
  8. Android引导页面去除白屏
  9. 对Android应用进行单元测试

随机推荐

  1. Android炫酷的3D球型标签云开源库 3dTagC
  2. Android(安卓)Switch控件介绍
  3. 安卓 打开app进入第一个指定的Activity
  4. Android_百度地图BaiduMap_LocationClien
  5. 韩老师安卓笔记
  6. Android 锁屏生命周期问题
  7. 今天开始写android的照片浏览器(一)至返回
  8. 整理 酷炫 Android(安卓)开源UI框架 动画
  9. Android NestedScrolling 嵌套滚动原理解
  10. 使用 Chrome 来调试你的 Android App