目前,随着APP行业的竞争越来越激烈,功能也越发丰富,人们评价一款产品的优秀程度,已经不能单纯通过 ”是否具备某些功能“ 来评价某个产品了,纯技术壁垒的产品已经是凤毛麟角,从用户角度,客户端不仅仅需要具备用户的基本诉求功能,流畅程度、发热程度、启动速度等等,分分钟可能让用户放弃你的APP,从而拥向你竞品APP的怀抱。
随着H5技术的快速发展,微信小程序的出现,使用前端相关语言开发,无论是“网页版”APP还是混合开发模式,市场占有率也不断增高,试想如果前端语言开发的产品和Native开发的产品,体验差不多,也就没有原生APP什么事情了, 毕竟开发成本摆在那。谁都不是sha zi,能用一份价格,搞定多个环境(Android手机、iOS手机、PC),相信老板们是很精明的。
那么作为一名Android APP的研发人员,我们应该从哪些角度来优化我们的APP以提高竞争力呢?又应该以什么“标准”来评价自己所做的优化工作是否合理呢?基于这些背景,我总结了一些关于产品优化的几个维度,如图:
1.CPU占用率
网友们也称作使用率,通俗的可以理解为APP在运行过程中,对于CPU资源的使用情况,若CPU使用率过高,会使整个手机无法响应用户,整体性能降低,影响用户体验,也容易引起ANR等问题,故我们的目标是APP正常运行的情况下,CPU使用率越低为好。
常用的测试方法:使用adb命令;使用Android studio profiler;使用各种云测平台;
方法1、基于adb shell dumpsys cpuinfo的方式,此方式可以测试手机中任意packageName的appadb shell "dumpsys cpuinfo | grep package" //其中package为具体应用的包名信息例如: adb shell "dumpsys cpuinfo | grep com.android.browser" //当前系统浏览器的情况复制代码
第一个参数:0.1%即为当前情况下,该应用的CPU使用情况,具体一目了然。
方法2、读取方 /proc/pid/stat的方式adb shell cat /proc/statLinux层有公共目录。很多公共信息资源由两个虚拟的文件系统提供: /proc:包括内存,CPU,网络等 /sys:设备驱动,网络环境(/sys/class/net/)等通过/proc这个伪文件系统,我们可以和内核内部数据结构进行交互,获取有关进程的有用信息。复制代码
具体的解释,可以看参考文档,里面有具体的说明,图中详细记载8核CPU的使用情况。
方法3、基于Linux的top命令adb shell top常用参数一般有如下: -m:表示需要展示的进程数目 -n:结束前需要刷新多少次 -d:刷新间隔(单位秒) -s:按照什么列排序(CPU,VSS,RSS,THR)输出的信息里面主要包括: PID(进程ID),CPU%(CPU使用率),VSS(虚拟内存使用量),RSS(实际物理内存使用量)等等。我们一般关心的数据列就是我们CPU%。例如:adb shell top -m 1 -d 0 -n 1复制代码
最直观的,应该是使用Android studio的Profiler工具了,具体代码中,出现的方法占用均有明确的信息(当然前提是APP是自己的,如果是别人的APP,用adb测试):
工具中,详细展示了各个进程的使用情况,通过record一段时间记录,来分析当前环境下,具体代码占用CPU的信息。
参考:
2、内存占用:
在Android系统中,每个APP进程除了同其他进程共享(shared dirty)外,还独用私有内存(private dirty),通常我们使用PSS(=私有内存+比例分配共享内存)来衡量一个APP的内存开销。移动设备的内存资源是非常有限,为每个APP进程分配的私有内存也是有限制。一方面我们要合理的申请内存使用,以免导致频繁的GC(垃圾回收机制)影响性能和大对象申请发生内存溢出;另一方面,我们要及时释放内存,以免发生内存泄漏。
关于内存涉及几个概念:
Terms VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存) RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存) PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存) USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS复制代码
参考:
常用的测试方法:使用adb命令;使用Android studio profiler;
方法1、基于adb shell dumpsys meminfo的方式,打印详细的当前环境PSS使用信息adb shell "dumpsys meminfo | grep package" //其中package为具体应用的包名信息例如: adb shell "dumpsys meminfo | 具体应用包名" //当前系统某个APP的总体情况复制代码
例如: adb shell dumpsys meminfo 具体应用包名 //当前系统某个APP的详细信息复制代码
方法2、基于adb方 shell top,将内存使用信息以文件方式储存后,再查阅步骤如下: (1)、adb shell top -d 1 > d://meminfo //将得到的数据输出到pc中d盘的,meminfo文件中 (2)、操作被测试的应用 (3)、中断,分析记录的数据,具体命令如下复制代码
如果是window的环境,可以使用cgywin,具体工具的安装过程,这里不做详解,来使用cat 命令过滤出想要的APP的内存使用情况信息,linux或者mac的小伙伴就没有这个烦恼了。
cat d://meninfo | grep 具体某个包名复制代码
另外,更高级的做法,是通过脚本,将meminfo信息,转换为Excel的分析报告,然后做每个时间段的对比分析图,这里不做方式的延伸,具体感兴趣的小伙伴可以自行查阅相关的资料。 最直观的,应该是使用Android studio的Profiler工具了,具体代码中,出现的方法占用均有明确的信息(当然前提是APP是自己的,如果是别人的APP,用adb测试):
发现没,和上面查看CPU情况的工具相同,没错,Android Studio 3.+以后,已经做了统一,且去留意sdk tools工具也少了很多bat,例如.9工具,MAT等等
工具中,详细展示了各个进程的使用情况,通过record一段时间记录,来分析当前环境下,具体代码占用内存的信息。
对于运行时内存的优化目标:占用越小越好,扫掉一切内存泄漏的场景
3、启动时间:
通俗理解为,用户打开APP,看到正常的画面,启动分为冷启动和热启动两种。
冷启动:应用程序首次启动,进程首次创建并加载资源的过程;
热启动:应用程序启动后点“back”键、“Home”键,应用程序退到后台,并未被完全“Kill”的状态,再次启动;
冷启动测试:
启动APP命令: adb shell am start -W -n package/activity停止APP命令: adb shell am force-stop packagepackage为应用的包名,activity为具体页面,启动测试可以是launcher配置的页面例如,测试浏览器的启动时间: adb shell am start -W -n com.android.browser/.BrowserActivity adb shell am force-stop com.android.browser复制代码
结果分析: ThisTime:596 这条信息中的时间就作为这次应用启动(冷启动)的耗时,单位是ms。
热启动测试:
启动APP命令: adb shell am start -W -n package/activity停止APP命令: adb shell input keyevent 3 (发送一个keyevent事件,3代表点击手机上的“back”键)例如,依旧是测试浏览器的启动时间: adb shell input keyevent 3 //关掉上面程序已经冷启动的页面 adb shell am start -W -n com.android.browser/.BrowserActivity //重新打开APP复制代码
结果分析:ThisTime:196 这条信息中的时间就作为这次应用启动(热启动)的耗时,单位是ms。
更加专业的手段,可以通过监控脚本实现,具体这里不做详细介绍,有兴趣的小伙伴可以另行查阅,资料还是蛮多的。 对于启动时间的优化目标:启动时间越小越好!没有最小,只有更小
4、流畅度:
应用的流畅度是最直接影响用户体验的,与之直接挂钩的概念为 FPS(每秒的帧数),Android系统里定义每秒大于60帧是流畅的(一帧的完成时间是16ms),通过帧率和流畅度曲线,可以帮助开发者分析是否存在UI问题。
平滑的完成一帧意味着任何特殊的帧需要执行所有的渲染代码(包括 framework 发送给 GPU 和 CPU 绘制到缓冲区的命令)都要在 16ms 内完成,才能保持流畅的体验。如果没有在期间完成渲染秒就会发生掉帧。掉帧是用户体验中一个非常核心的问题,丢弃了当前帧,并且之后不能够延续之前的帧率,这种不连续的间隔会容易会引起用户的注意,也就是我们常说的卡顿、不流畅。
测试方法:
方法1、开发者模式下:“GPU呈现模式分析”, 只要下方的曲线不超过绿线,都可以视之为流畅方法2、Android设备的“设置”->"开发者选项",然后勾选“GPU呈现模式分析”,勾选adb shell dumpsys gfxinfo,记录信息到文件中(不同品牌手机路径可能不同) 例如:adb shell dumpsys gfxinfo {具体某个应用的包名} > d://fps.txt 将某个应用的信息记录到d盘fps文件中复制代码
打开生成的fps.txt,找到Profile data in ms这部分数据,也可以将数据添加到Excel中查看"Draw"" : 创建显示列表(display lists,记录所有view对象的绘制指令)的时间开销。"Process" : 执行显示列表中绘制指令的时间。UI视窗中的View数量越多,需要执行的绘画命令就越多。"Execute" : 将一帧图像交给合成器compostior的时间。这部分占用的时间通常比较少复制代码
通过图表方式,就很直观了,建议直接测试较复杂的页面,发现Execute这一列明显执行时间变长(大于16ms)后,就得留意看看具体APP对应的页面如何优化了,通过记录数据,与优化前的数据进行对比(例如做个折线图),也能清晰的分析自己的成果。
方法3、在程序中添加BlockCanary,监控程序的卡顿情况,这块资料比较多,不做详细介绍;复制代码
对于流畅度的优化目标:控制一帧的完成时间在16ms内
5、过度绘制:
提到过度绘制,相信很多人都知道,产生的原因,一方面是设计的复杂性,但主要还是和研发人员所采用的具体方案决定。常说条条大路通罗马,其实想实现UI的功能,方法很多,走错路也很常见,对于过度绘制的测试主要通过人工进行测试,系统是不会报错的,一不小心就创建了一个多层套用的布局,想从根本上解决问题,还得从健康的习惯开始,首先我们先来几张熟图(被盗来盗去的)。
蓝色:1倍过度绘制绿色:2倍过度绘制浅红色:3倍过度绘制深红色:4倍过度绘制及以上复制代码
测试方法有:
方法1、打开开发者选项中的显示GPU过度绘制来进行测试(PS:只有Android4.2及以上的版本才具备此功能),直接在手机上查看结果;方法2、使用Android Studio的Tools——>Layout Inspector 首先启动APP后,打开Activity,然后再使用Inspector分析复制代码
这个是使用了分析后的结果,可以看到明显的存在过度绘制的问题,套用层级过多,如使用约束布局,或者自定义View来绘制Item,则可以减少很多层级关系。
凡事得有个标准,我们才能更好的来优化,所以比较流行的验收的标准(网友版)为: (1)、不允许出现黑色像素; (2)、不允许存在4x过度绘制; (3)、不允许存在面积超过屏幕1/4区域的3x过度绘制(淡红色区域)复制代码
以上是我们自己的APP的分析,那么我们如何来分析别人的APP呢?
方法3、使用 “uiautomatorviewer” 测试; 从Android studio 3.1以后会发现没有了DDMS,用uiautomatorviewer可以替代它,路径在:Android SDK->tools->bin->uiautomatorviewer,启动后,可能会遇到“Unable to connect to adb. Check if adb is installed correctly”(我遇到了),此时,将uiautomatorviewer.bat,用NotePad++等工具打开,将文件最后一行的bindir路径修改为SDK的platform-tools所在路径(注意,用完后,还得还原回来,我测试用win10的操作系统,在platform-tools使用后,发现Android studio中Layout Inspector启动不了,下图配置还原后又恢复了)复制代码
上图,是我拿手机某个优秀APP来分析的案例,通过UI工具,可以很清晰的,分析别人做的产品使用了哪些布局技术,以及他们的层级关系。通过与优秀的APP做对标,我们来优化自己的APP,目标也就很清晰了。
对于过度绘制的优化目标:减少冗余的层次使用,活用相对布局、约束布局、自定义VIew,ViewGroup、活用Merge,ViewStub,include等等手段。
6、响应时间:
我们通常所说的APP卡、慢通常就是由于页面响应时间过长导致的。
可供参考的标准: 优秀:0~400ms; 标准:400ms~2000ms; 轻微隐患:2000ms~5000ms; 严重隐患:5000ms以上。复制代码
测试方法:
方法1、:使用 “logcat” 测试页面启动时间;方法2、:使用第三方的云测工具,市面上很多,选择一款即可 例如:OneAPM, https://www.oneapm.com/ 华为DevEco, https://deveco.huawei.com/ 百度测试, http://bce.baidu.com/product/mat.html Testin, https://www.testin.cn/复制代码
这里主要介绍下方式1,logcat这个控制台,相信大家都非常熟悉,通过过滤tag,display即可得到结果。772ms即为此次开启页面所消耗的时间
对于响应时间的优化目标:当然是越短内越好啦!
7、稳定性:
安卓稳定性标准,有两大指标:
(1)、闪退率,闪退率包括java的闪退率以及native闪退率;
(2)、ANR,就是应用没有响应。
主要分两个方面:
(1)、启动崩溃率, 公式 = 应用中一天发生闪退总数 / 应用一天中总的启动次数;
(2)、用户或设备崩溃率, 公式 = 应用一天中发生的闪推用户数(去重)/ 应用一天中总体的活跃用户数;
测试方式:1、上线前(Monkey测试、华为终端云测、Testin云测等等);2、上线后(友盟统计、腾讯Bugly等等);
而阿里推荐的优秀的产品,参考标准为:1、参考启动崩溃率:0~0.15%;2、参考设备崩溃率:0~0.2%。与上图的听云的数据大体相同,可以认为是行业的一个标准。
参考数据来源:
作为开发人员,我们平时在打包Release包后,使用Monkey测试下,还是比较推荐的一种做法(基于测试员和工程师的友好相处法则),而Monkey测试具体标准是需要覆盖70%以上的页面,包括登陆,非登陆状态,并且经过八个小时不崩溃,平时如果是时间比较赶,短时间验证后及时修正明显的问题后,交付专业测试即可。
Monkey测试的方法: 1、在adb的目录,使用adb shell monkey测试; 2、如果Android studio已经配置了adb的路径,可以直接在Terminal控制台,输入指令;具体指令为(指令比较多,这里只罗列了几个): adb shell monkey -p {包名} -v {后面的测试的次数} –throttle {延迟时间} > {测试文件路径}例如: adb shell monkey -p xxx.xxx.xxx -v 100 –throttle 300 > D:\MTestLog.txt 针对包名为xxx.xxx.xxx的产品进行测试,每个事件执行时间为300ms,执行100个事件,并将测试报告输出到d盘的MTestLog.txt文件上 如果,monkey设置运行时间过长,期望中断,则可以按如下命令执行: (1)、adb shell (2)、ps | grep monkey 得到monkey进程号 (3)、kill pid [刚才查到的进程号]复制代码
monkey -help:可查看参数说明列表。参数选项有:(1) -p 包名1 -p 包名2 … :指定一或多个待测试的包,若不指定则测试中可打开任意APP;(2) -v:指定打印日志的详细程度,有‘-v’,'-v -v','-v -v -v'三个级别;(3) -s seed值:在测试中,虽然用户操作序列(每次操作按一定的顺序所组成的一系列操作)是随机生成的,但只要对同一个包指定相同的Seed值,就能使测试事件相同,可用于排错。所以也说这个操作序列是伪随机的。若不添加此参数,结果中会自动生成seed值;(4) –-throttle 毫秒数:指定操作(即事件)间的时延,单位是毫秒;(5) –-ignore-crashes:使得操作序列可以全部执行完,而不会在发生崩溃时就终止程序进程;(6) –ignore-timeouts:使得操作序列可以全部执行完,而不会在发生ANR(Android Not Responding)时就终止程序进程;(7) –-ignore-security-exceptions:使得操作序列可以全部执行完,而不会在发生许可错误时(如证书许可,网络许可等)时就终止程序进程;(8) –-kill-process-after-error:使得当应用程序发生错误时停止运行并保持在当前状态,即仅是静止在发生错误时的状态,而不是结束该应用程序的进程;(9) –-monitor-native-crashes:指定监视并报告应用程序发生崩溃的本地代码;(10)–-pct-事件1 事件百分比1 -pct-事件2 事件百分比2 … :用于指定某类事件数目占总事件数目的百分比;其中所指定事件参数选项可为: -flip(点击事件) -touch(触摸事件是一个down-up事件) -motion(动作事件由一个down事件、一系列的伪随机事件和一个up事件组成) -trackball(轨迹事件,在屏幕上进行随机拖动) -nav(基本导航事件,如上下左右键) -majornav(主要导航事件,通常引发图形界面中的动作) -syskeys(系统按键事件指如系统按键Home、Back、Start Call、End Call及volumeControl) -appswitch(启动activity事件) -anyevent(其它类型事件) 注意,各事件类型的百分比总数不能超过100复制代码
实际开发过程,可以通过脚本,例如Python等,指定点击设备的具体位置,流程化的进行测试验证,具体可以参考:
//含scriptfile、python
稳定性优化的目标:崩溃率控制在优秀的范围内,越小越好,且问题越早发现越好,上线前做下灰度测试也是不错的选择。
8、消耗电量:
相对于PC来说,移动设备的电池电量是非常有限的,保持持久的续航能力尤为重要。Android的很多特性都比较耗电(如屏幕,GPS,sensor传感器,唤醒机制,CPU,连网等的使用),我们必须要慎重检查APP的电量使用,以免导致用户手机耗电发热,带来不良体验。
测试方法,测试手机安装目标APK前后待机功耗无明显差异, 常见使用场景中能够正常进入待机,待机电流在正常范围内。长时间连续使用应用无异常耗电现象;电量测试的方法分为软件测试和硬件测试两类。 测试手段包含软件测试和硬件测试两类,这里主要介绍下软件测试的手段:
方法1、通过BatteryManager发的广播来做分析,可参考:http://hukai.me/android-training-course-in-chinese/performance/monitor-device-state/battery-monitor.html方法2.1、adb shell dumpsys battery来获取,本地分析方法2.2、historian.py脚本 + Python环境 (1)、下载historian.py脚本,下载地址:https://github.com/google/battery-historian (2)、依旧是方式1,系统方式,记录日志文件,初始化batterystats数据 adb shell dumpsys batterystats--reset (3)、拔掉手机,操作APP,操作完成后,重新连接手机,执行下面的命令,收集系统整体的Battery数据 adb shell dumpsys batterystats > batterystats.txt (4)、得到这些数据后,这个时候使用我们的battery-historian来生成我们可见HTML报告 python historian.py batterystats.txt > batterystats.html (5)、浏览器打开,分析具体结果复制代码如图,level值就是电量,取测试结束后和测试开始时的电量做差,就是我们要得到的测试过程中电量的消耗
方法3、使用腾讯的GT APP,操作选中APP后,将生成的.csv文件保存到本地,再进行分析,如图停止后,获取生成的文件,SD卡目录,找到GT文件夹,具体的分析过程,不在此详细描述复制代码
参考:
方法4、采用市场上提供的第三方工具复制代码
耗电量的优化目标,根据不同APP的情况,视频类,游戏类等等,寻找发热原因,优化代码才是关键
9、安装包大小:
包体大小能被列为性能指标,是从APP性能指标及运营两个维度考虑的,用户是更希望包体小的同时性能要好,有时它们会是一个互相取舍的关系。对Android Apk包的资源文件进行解析,分析冗余资源文件,可压缩资源文件,并计算可压缩的比例。
优化方法:
方法1、通过Android studio的分析工具,删除冗余的“历史资源”复制代码
方法2、通过Gradle的配置release { minifyEnabled true //proguard 删除所有未使用的方法和指令 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), "proguard-rules.pro" debuggable false jniDebuggable false renderscriptDebuggable false pseudoLocalesEnabled false zipAlignEnabled true}//应用不需要支持国际化,那么可以设置 resConfigs 为 "zh","en",对于官方的 support library,默认是支持国际化的,也就是包含了很多不同语言的资源文件defaultConfig { //... resConfigs "zh","en"}方法3、通过将res中的png、jpeg等图片,可考虑转为webp格式使用复制代码
10、消耗流量:
目前的网络类型包含2G、3G、4Gwifi,其中还有不同运营商的区分,我们在APP的使用中经常遇到大资源,重复请求,调用响应慢,调用失败等各种情况。在不同的网络类型之下,我们不仅要控制流量使用,还需要加快请求的响应。另外,对于需要联网的手游来说,部分游戏对不同联网方式的网络类型采用了不同的流量消耗策略,主要分为wifi环境和蜂窝网络环境。我们可以根据实际采用的方案来决定具体是否分开两种环境测试。
测试方法:
获取某个应用的PID,再获取该pid的消耗流量值首先要获取进程的ID,命令:adb shell "ps | grep 具体包名"然后获取报告:adb shell "cat/proc/pid/net/dev" 注意替换这个pid为上一行命令获取的pid复制代码
此应用一共三个进程,其中包含两个独立服务,我们获取pid=16746来进行测试
得到的结果中只需要关注两个值:Receive(app接收到的数据)、Transmit(app发出的请求数据);流量等于这两个值的和:Receive+Transmit,取有效值Receive和Transmit相加即可。总结:
我们做性能测试的时候,不仅要发现问题,也要定位问题,深入挖掘性能问题的根源才是我们需要持续努力的方向,通过对比其他公司的优秀产品,分析出自身APP的不足,来优化我们的代码,通过竞品分析,可以避免闭门造车,不断完善我们的方案。
另外,除了上文中提到的云测试网站,一些可用的测试工具有:
iTest:
能够记录特定应用的性能消耗情况,包括CPU、内存、流量、电量等信息,支持浮窗实时查看应用的具体信息,iTest不需要集成SDK到应用中,在iTest中选中需要测试的应用即可进行测试;
Emmagee:
网易开发的性能检测工具,Emmage和iTest一样,不需要在应用中集成SDK,能够对应用的常用性能指标进行检测,并以csv的格式保存方便查看应用的各项参数;
Tencent GT:
腾讯系的测试工具,直接安装到手机上即可。
参考:
《Android APP性能测试小结(7个性能指标》
《Android FPS流畅度测试》
《APP性能测试的6项关键指标及测试获取手段》
《那些年我们用过的显示性能指标》
《Android应用性能评测调优》