文章目录:
- 什么是性能测试
- 为什么要做性能测试
- App性能测试指标是什么
- 如何使用Profiler工具进行性能测试——以结对编程作业为例
- 关于App性能优化的一些建议
1. 什么是性能测试
性能测试针对系统的性能指标,建立性能测试模型,制定性能测试方案,制定监控策略,在场景条件之下执行性能场景,分析判断性能瓶颈并调优,最终得出性能结果来评估系统的性能指标是否满足既定值。
2. 为什么要做性能测试
1)目前绝大多数应用都是基于网络的分布式应用,我们无法知道用户数量,用户场景的不确定性,导致系统测试时,不仅仅是功能,业务逻辑,接口测试,还要测试系统性能。一个用户没问题,但是用户一旦多了就可能出现各种各样的问题,所以需要进行系统性能测试。
2)用户数量增加,系统负载增加,进行系统性能测试,知道系统承受的并发用户数量,带宽是否够用,cpu是否够用,内存是否够用,硬盘速度是否跟得上。从服务端来看,测试服务器是否能承载用户多并发,系统是否稳定,从用户角度看响应时间速度。
3. App性能测试指标是什么
- APP启动速度(启动时间)
- CPU的占用量
- 电量消耗
- 内存占用
- FPS(每秒传输帧率)直接反应APP流畅度
- 流量
- CPU过度渲染
在Profiler里,主要的性能指标有:cpu占用、内存占用、流量、电量消耗。由于我的app不涉及联网功能,我们将重点围绕cpu、内存展开测试。
4. 如何使用Profiler工具进行性能测试
4.1 Profiler使用流程
打开Android studio,选择结对编程的项目,点击界面右上角的速度表图标或者左下角的Profiler图标,弹出Profiler工具界面,如下图所示
现在我们没有运行虚拟机,也没有读取捕获的片段,所以界面是空的。
现在单击运行按钮,启动虚拟机并运行我们的app。可以看到,进程被自动加载到了profiler当中。左边是我们正在运行的进程,右边是实时的性能数据。我们可以清楚地看到虚拟机的实时运行情况。
界面中绘制了四张曲线图,分别对应CPU(处理器)、Memory(内存)、Network(网络)、Energy(耗电)四个指标,想要查看某个指标的具体信息,只需要点击曲线图即可。
下面介绍不同性能指标工具的详细使用。
4.2 CPU分析工具(CPU Profiler)
进入Cpu Profiler,我们可以看到如下界面。
它会立即开始显示应用程序的CPU使用率和线程的活动
CPU Profiler的默认视图包含以下内容:
①:Event timeline:显示应用在他们的生命周期中不同状态间转换时的活动,并指示用户与设备的交互,包括屏幕旋转事件
②:CPU timeline:显示应用程序的实时CPU使用情况(占可用CPU总时间的百分比)以及应用程序正在使用的线程总数。时间表还显示了其他进程(如系统进程或其他应用程序)的CPU使用情况,因此您可以将其与应用程序的使用情况进行比较。可以通过沿着时间轴的横轴移动鼠标来检查历史CPU使用率数据。
③:Thread activity timeline:列出属于你的应用程序进程的每个线程,并使用下面列出的颜色在时间线上指示其活动。记录方法跟踪之后,可以从此时间线中选择一个线程,以在跟踪窗格中检查其数据。
绿色:线程处于活动状态或准备好使用CPU。也就是说,它处于“运行”或“可运行”状态。
黄色:线程处于活动状态,但它正在等待I / O操作(例如磁盘或网络I / O),然后才能完成工作。
灰色:线程正在休眠,不占用任何CPU时间。当线程需要访问尚不可用的资源时,有时会发生这种情况。线程进入自愿睡眠,或者内核使线程进入休眠状态,直到需要的资源变为可用。
④:Recording configurations:允许您选择以下选项之一来确定探查器如何记录方法跟踪。
Sampled(采样):一个默认配置,可以在应用程序执行期间频繁地捕获应用程序的调用堆栈。分析器比较捕获的数据集合以获取关于应用代码执行的时间和资源使用信息。基于抽样的跟踪的一个固有问题是,如果您的应用程序在捕获调用堆栈之后输入方法,并在下次捕获之前退出该方法,则该方法调用不会被分析器记录。如果您对如此短的生命周期跟踪方法感兴趣,则应使用检测跟踪。
Instrumented:默认配置,在运行时测试您的应用程序,以在每个方法调用的开始和结束时记录一个时间戳。收集时间戳并进行比较,以生成方法跟踪数据,包括定时信息和CPU使用情况。请注意,与每种方法相关的开销都会影响运行时性能,并可能影响分析数据 - 对于生命周期相对较短的方法来说,这一点更为明显。此外,如果您的应用程序在很短的时间内执行大量方法,分析器可能会快速超出其文件大小限制,并且无法记录任何进一步的跟踪数据。
Edit configurations:允许您更改上述采样和检测记录配置的某些默认设置,并将其保存为自定义配置。
⑤:Record button:开始和停止录制方法跟踪按钮
记录和检查方法跟踪
对于应用程序进程中的每个线程,你可以找到在一段时间内执行哪些方法以及每个方法在执行期间消耗的CPU资源。你还可以使用方法跟踪来识别调用者和被调用者,调用者是一种调用另一种方法的方法,被调用方是另一种方法调用的方法。你可以使用此信息来确定哪些方法太频繁地调用特定资源繁重的任务,就可以尝试优化应用程序的代码以避免不必要的工作。
要开始记录方法跟踪,从下拉菜单中选择Sampled或Instrumented类型,然后单击Record开始进行记录,完成后点击Stop recording停止记录。profiler自动选择记录的时间帧,并在方法跟踪窗格中显示它的跟踪信息,如下图所示。如果要检查不同线程的方法跟踪,只需从线程活动时间轴中选择它。
app生成1000道题的记录情况如下:
点击线程左边的小三角可以查看详细信息。
主线程的信息如下:
点击方法对应的长条还会显示方法的具体信息。长方形越长代表方法耗时越久。
4.3 内存分析工具(Memory Profiler)
可以通过左上角的下拉菜单切换不同的工具。我们选择Memory。
默认显示的界面如下:
内存剖析器的默认视图包括以下内容:
①:强制执行垃圾收集事件的按钮
②:捕获堆转储的按钮
③:一个记录内存分配的按钮,当连接到运行Android7.1或更低的设备时,该按钮才会出现
④:放大/退出时间线按钮
⑤:可以跳转到实时内存数据的按钮。
⑥:事件时间轴,显示活动状态、用户输入事件和屏幕旋转事件。
⑦:内存使用时间线,包括以下内容:一个堆叠图,显示每个内存类别的内存大小,如左侧的y轴和顶部的颜色键。虚线表示已分配对象的数量,如右侧的y轴所示。每个垃圾收集事件的图标
内存计算指标
左上角能看到一栏数据,显示了不同的内存计算指标。
它们分别代表以下内容:
Java:从Java或Kotlin代码中分配的对象的内存
Native:从C或c++代码中分配的对象的内存,即使你没有在app中使用c++,你可能会看到一些本地内存,因为Android框架使用Native内存来处理各种任务,比如处理图像资产和其他图形——即使你写的代码是Java或Kotlin
Graphics:用于图形缓冲区队列的内存用于显示屏幕上的像素,包括GL表面、GL纹理等。(注意,这是与CPU共享的内存,而不是专用的GPU内存)
Stack:在你的应用程序中,Native和Java栈使用的内存。这通常与你的应用程序运行的线程数有关
Code:您的应用程序用于代码和资源的内存,如dex字节码,优化或编译的dex代码。所以库和字体
Other:应用程序使用的内存,系统不确定如何分类
Allocated:应用程序分配的Java/Kotlin对象的数量。这并不计算用C或c++分配的对象
记录某一时刻的内存分配情况
如果我们想要记录app在某段时间的内存占用情况,应该怎么做呢。
方法很简单。选择左边录制选项中的Record Java/Kotlin allocations,点击Record,工具便开始记录内存占用情况。
需要结束录制时,点击红色圆圈即可。
随后录制结果便会被保存,你可以在左侧菜单中找到它,点击便可查看记录的数据。
以生成题目的过程为例,记录的内存占用如下:
或者你也可以以可视化图形的方式展现内存占用情况,我个人更加喜欢这种方式。
将鼠标悬停在彩色长方形上,我们可以很清楚地看到不同的类占用的内存大小,长方形越长,内存占用越大。
以我的app为例,我可以看到,在我自己编写的类中,GeneratorFragment占用了最多的内存。以此为依据,我们便可以针对性地优化个别模块。
5.关于App性能优化的一些建议
除了利用性能测试工具分析应用性能,寻找优化空间,开发者在开发过程中,就应该注意一些性能优化技巧,养成良好的开发习惯,减轻性能优化的负担。
下面是一些优化建议:
-
在LinearLayout和RelativeLayout都可以完成布局的情况下优先选择LinearLayout,可以减少View的层级,但是注意相同组件可能RelativeLayout绘制时间长
-
onDraw中不要创建新的局部对象,onDraw方法中不要做耗时的任务。
-
不要在主线程进行网络访问/大文件的IO操作。
-
绘制UI时,尽量减少绘制UI层次;减少不必要的view嵌套。
-
当我们的布局是用的FrameLayout的时候,我们可以把它改成merge,可以避免自己的帧布局和系统的ContentFrameLayout帧布局重叠造成重复计算(measure和layout)。
-
在view层级相同的情况下,尽量使用 LinerLayout而不是RelativeLayout;因为RelativeLayout在测量的时候会测量二次,而LinerLayout测量一次,可以看下它们的源码。
-
删除控件中无用的属性。
-
布局复用.比如listView 布局复用。
-
尽量避免过度绘制(overdraw),比如:背景经常容易造成过度绘制。由于我们布局设置了背景,同时用到的MaterialDesign的主题会默认给一个背景。这时应该把主题添加的背景去掉;还有移除XML 中非必须的背景。
-
自定义View优化。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。也是避免过度绘制。
-
启动优化,启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。比如闪屏页面,合理优化布局,加载逻辑优化,数据准备。
-
合理的刷新机制,尽量减少刷新次数,尽量避免后台有高的 CPU 线程运行,缩小刷新区域。