Android官网使用指南性能:https://developer.android.com/topic/performance
APP的启动过程
调用起APP、创建一个空白窗口、启动一个进程 ——> Application OnCreate 开始调用起App ——> Activity OnCreate 创建主线程MainThread(页面的渲染,数据的初始化)
启动性能指标
冷启动:第一次启动,无任何进程和数据
暖启动:之前启动过,存在缓存的数据
热启动:进程都还存在
首屏启动:页面完全加载完全的时间
对于以上的建议时间
Cold startup takes 5 seconds or longer 冷启动不超过5s
Warm startup takes 2 seconds or longer 暖启动不超过2s
Hot startup takes 1.5 seconds or longer 热启动不超过1.5s
1、adb logcat 查看日志
2、录屏+视频拆祯
3、uiautomator等自动化工具200ms巡检界面变化
4、traceview
5、硬埋点 页面加载加了获取时间性能的埋点
logcat使用
package=com.xueqiu.android
清理缓存数据:adb shell pm clear $package
停止进程:adb shell am force-stop $package
执行上面的两个行为后,让启动的app变得冷启动
启动app:adb shell am start -S -W $package/.view.WelcomeActivityAlias
获取数据:adb logcat | grep -i displayed
查看结果
1、startTime: 记录刚准备调用startActivityAndWait()的时间
2、endTime: 记录startActivityAndWait()函数调用返回的时间点
3、WaitTime:startActivityAndWait()调用耗时 WaitTime = endTime - startTime
ffmpeg拆祯方法
作用:计算每个阶段时间
原理:先把APP停止 - 使用录屏录制启动的过程 - 获取到录制视频后(这时在模拟器),把获取到的视频拉取到本地电脑 - 然后拆祯为gif图 - 再拆成jpg图,通过1s的图拆成多少份后,计算多少张图片完成了这个加载的动作。然后用图的数量*1s拆成的分数,得出这个动作所消耗的时间。
使用说明:
package=com.xueqiu.android 测试的包名
adb shell am force-stop $package 停止测试软件
adb shell screenrecord --bugreport --time-limit 30 /data/local/tmp/Test.mp4 & 录屏30s
adb shell am start -S -W $package/.view.WelcomeActivityAlias wait
adb pull /data/local/tmp/Test.mp4 拉取录制的视频到本地
ffmpeg -i Test.mp4 Test.gif 视频拆成gif图片
ffmpeg -i Test.mp4 -r 10 frames_%03d.jpg 把gif图1s的图片拆成10份(也就是一张图片用例0.1s)
开发者工具
蓝色线:DOM出现
红色线:资源加载完成
Disable cache:加载缓存
fiter过滤表达式
domain.展示domain中的资源,*.com
has-response-header.包含指定HTTP响应header
is. 表达式
larger-than.展示大于某个尺寸的资源,1000等于1k
method.指定http请求方法,比如get或者post
mime-type.资源mime类型,比如application/json
scheme.HTTP(scheme.http)或者HTTPS(scheme:https).
status-code.状态码
performance使用
加载详情
对应字段说明:https://developers.google.com/web/tools/chrome-devtools/network/reference?utm_source=devtools#timing-explanation
手机浏览器性能分析
原理:通过inspect把手机中的webview页面加载到PC端的浏览器中,然后通过浏览器的开发者工具查看
inspect使用注意点
1、模拟器使用6.0默认支持
2、物理机需要打开app内开关
3、PC端使用的chrome浏览器版本62.XXX (防止控制台样式异常)
W3C性能说明:https://www.w3.org/TR/navigation-timing/
资源加载指标说明
prompt for unload:访问一个新页面时,旧页面卸载完成的时间
redirect:重定向,用户注销登陆时返回主页面和跳转到其他的网站等
app cache:检查缓存,是否打开
DNS(域名系统):DNS查询时间,如果时长连接或者请求文件来自缓存等本地存储则返回fetchStart时间点
TCP:与服务器建立链接的时间
request:浏览器发起请求的时间
response:拿到第一个响应字节到最后一个响应字节的时间
processing:各种状态的时间点
load:触发load事件执行的时间
获取单个页面资源的性能
window.performance.timing
window.performance.navigation.type
跳转 0
刷新 1
后退 2
JS模拟的基本操作
window.history.back() // 返回
window.history.forward() // 前进
window.location.reload() // 刷新
window.location.href="https://www.baidu.com/" //跳转到某个页面
window.location.href // 查看本地的url
from appium import webdriver
from selenium.webdriver.common.by import By
def test_APP():
desire_cap = {
"platformName": "android",
"deviceName": "emulator-5554",
"appPackage": "com.xueqiu.android",
"appActivity": ".view.WelcomeActivityAlias",
"noReset": True, # 保存缓存, 热启动
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desire_cap)
driver.implicitly_wait(16)
driver.find_element(By.XPATH,"//*[@text='交易']").click()
webview = driver.contexts[-1]
driver.switch_to.context(webview)
print(driver.execute_script("return window.performance.navigation.type"))
driver.execute_script("window.location.href='https://www.baidu.com/'")
driver.execute_script("window.location.reload()")
print(driver.execute_script("return window.performance.navigation.type"))
方式一、systrace SDK 自带分析工具
1、位置:sdk/platform-tools/systrace
2、需要使用python2.7
3、启动过程遇到少的文件,按需install
使用
1、启动设备:python systrace.py -e 192.168.181.102:5555 -l
不加参数启动开始录制:python2 systrace.py
2、----运行模拟器上的对应要测的Activity----
3、停止录制手机报告:按下enter
4、在sdk/platform-tools/systrace获取报告trace.html
结果查看
详细信息
1、帧点
绿色:16.6ms内,黄色,红色超过16.6ms
2、任务状态
灰色:休眠 蓝色:可运行 橙色:不响应
3、函数调用
方式二、dumpsys gfxinfo
adb shell dumpsys gfxinfo com.xueqiu.android
adb shell dumpsys gfxinfo com.xueqiu.android
from matplotlib import pyplot
def test_draw():
import subprocess
cmd = "adb shell dumpsys gfxinfo com.xueqiu.android"
res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
# print(str(res.stdout.read(),encoding='utf-8'))
lines = res.stdout.readlines() # readlines()获取的是list
# print(lines)
i = 1
# 判断下是否是对应的包
for line in lines:
i += 1
if "com.xueqiu.android.common.MainActivity" in line.decode("utf-8"):
break
# 使用列表生成式,对获取的数据处理,并获取新的lines列表
lines = [data.decode("utf-8").replace("\r\n","").replace("\t"," ").strip() for data in lines]
lines = lines[i:i + 120] # 获取i行到i+120行的数据
print(lines)
datas = [[] for row in range(4)] # 生成一个空的二维数组 [[],[],[],[]]
# lines = [14.84 0.2 6.17 11.58,14.00 0.4 6.66 11.11,.....]
for x in lines:
datas[0].append(float(x.split()[0])) # 获取lines中每组数据的第1个值 14.8,并且添加到datas[[14.8,],[],[],[]]
datas[1].append(float(x.split()[0])) # 获取lines中每组数据的第1个值 14.8,并且添加到datas[[14.8,],[0.2],[],[]]
datas[2].append(float(x.split()[0])) # 获取lines中每组数据的第1个值 14.8,并且添加到datas[[14.8,],[0.2],[6.17],[]]
datas[3].append(float(x.split()[0])) # 获取lines中每组数据的第1个值 14.8,并且添加到datas[[14.8,],[0.2,],[6.17,],[11.58,]]
# print(datas)
# 绘制成图
fig = pyplot.figure() # 生成一个画板
# num = 1
# for n in datas:
# ax = fig.add_subplot(2,2,num) # 生成一个2*2图形
# ax.plot(n) # 绘制第一个数组的图 plot折线
# num += 1
# pyplot.show()
# 分组绘制4个图 如果绘制到一个图中,fig.add_subplot(X,X,1),最后一个数字都改为1
ax1 = fig.add_subplot(2,2,1)
ax1.plot(datas[0]) # 折线图
ax1.set_title("Draw") # 标题
ax1 = fig.add_subplot(2,2,2)
ax1.scatter(range(120),datas[1]) # 散点图
ax1.set_title("Prepare")
ax1 = fig.add_subplot(2,2,3)
ax1.hist(datas[2],range(5)) # 柱状图
ax1.set_title("Process")
ax1 = fig.add_subplot(2,2,4)
ax1.plot(range(120),datas[3],'--k') # 虚线图
ax1.set_title("Execute")
pyplot.show()
datas处理后数据形式
注意 ⚠️ : gfxinfo信息会有实效性,时间长了就获取不到了,注意刷新和模拟器中开发者选项中“GPU呈现模式分析”
内存问题 (内存抖动、full gc)
cpu (计算耗时)
render(布局复杂、overdraw)
冰冻帧:一个帧超过0.7s (橙色、红色)
帧分析:adb -s devicesname shell dumpsys gfxinfo | less
CPU与GPU关系
1、图形API不允许CPU直接与GPU通信,会通过Graphics Driver中间件进行连接
2、过程:CPU进行数据的处理 —> 处理后到Graphics Driver,Graphics Driver会维护一个队列,CPU把display list放入队列。—> GPU从队列去数据进行绘制
GPU渲染工具
Android开发者工具提供性能调优工具
GPU渲染分析:GPU-RENDERING-PROFILE
GPR显示内容
1、绘制每一帧所消耗的时间
2、不同的颜色代表UI绘制的不同阶段
3、并且子啊柱状图的中间还有一根绿色的横线代表16ms的绘制时间基准
4、GPR会统计并显示app最近运行的128帧
蓝色
View需要先转换为GPU能识别的格式(中间件处理的过程)
蓝色较高
橙色
CPU在等待GPU完成工作 (GPU绘制的过程)
橙色较高
GPU任务负载过高,view绘制太复杂
不同颜色的意义(Android官网https://developer.android.com/topic/performance/rendering/inspect-gpu-rendering)
内存耗用名词
指标解析
VSS:衡量虚拟内存大小五太大用处。无法知道分配的物理内存大小
RSS:个进程的RSS相加,会超过系统内存使用量(不是按比例分配)
PSS:个进程的PSS之和,就是系统的内存使用量 (按比例分配的)
USS:是PSS中自己的部分,不包含任何共享的部分 (app之间交换使用共同的数据会产生共享的内存)
内存占用大小规律:
VSS>=RSS=>PSS>=USS
查看内存第一种方式:procstats
adb shell dumpsys procstats --hours 3 (查看3小时内的内存使用情况)
输出信息
输出格式
进程详情
进程名称/USER/VersionCode
状态:minPSS-avgPSS-maxPSS/minUSS-avgUSS-maxUSS
输入字段解释
百分比:表示在总的时间内,进程在各种状态下的消耗
例如:100%,就指在这段时间内,这个进程是一直处于运行当中的
TOTAL:表示进程的总和占用情况
Img Fg:加载到前台
Service:标示了是否是服务
Persistent:标识了是否一直驻留子在内存当中,与Service一样,标示内存进驻的级别
Top:标识了是否是顶层进程
Receiver:标识了是否是广播进程
查看内存第二种方式:meminfo
查看指定的进程mem:
adb shell dumpsys meminfo com.xueqiu.android(包名)
网络分析
adb shell dumpsys netstats
分块展示
Activity interfaces:活动接口
Activity UID interfaces:活动UID接口
Dev statistics:开发统计信息
Xt statistics:Xt统计信息
UID statistics:UID统计信息
UID tag statistics:UID代码统计信息
活动接口和活动UID接口
开发统计信息/Xt统计信息
UID统计信息
获取对应应用UID
adb shell dumpsys package com.xueqiu.android | grep userId
输出:userId=10000 gids=[2002,1023,3134]
查看相关应用的流量情况
set=DEFAULT 表示前台网络使用情况
set=BACKGROUND表示后台使用情况
set=ALL表示上述两类使用情况
tag=0x0表示与流量关联的套接字代码
rxBytes 和 rxPackets 表示在相应时间间隔内接收的字节数(rb)和数据包数(rp)
txBytes 和 txPackets 在相应时间间隔内发送的字节数(tb)和数据包数(tp)
查看对应网络的情况
如果使用模拟器会出现报错,可以换成真机
使用过程
adb shell dumpsys package com.xueqiu.android | grep userId 获取userId
adb shell dumpsys package netstats | grep 12321(UID信息) 获取网络信息,并且使用userId过滤
目的:通过不同的测试场景,找出APP高耗电的场景解决
常见电量问题
唤醒锁(acquire)
关闭屏幕显示,让CPU后台运行
app长期获取唤醒锁,不释放
阻止设备进入低电量模式
耗电标准
电池会话:再次充满电之间
消耗整个电池会话的0.70%
在后台运行时,消耗整个电池会话的0.10%
调用唤醒锁存在的问题
void doSomethingAndRelease() throwsMyException{
wakeLock.acquire();
doSomethingThatThrows(); //当这里出现报错的时候,下面锁的释放将无法关闭。导致其他应用无法使用
wakeLock.release();
}
改进
void doSomethingAndRelease() throwsMyException{
try{
wakeLock.acquire();
doSomethingThatThrows();
}finally{
wakeLock.release();
}
}
方式一、测试性能使用Google Playhttps://play.google.com/store/apps
方式二、Battery Historian工具
GitHub地址:https://github.com/google/battery-historian
安装
注意:要在python2.7环境下执行,安装好go语言
git clone https://github.com/google/battery-historian.git
cd battery-historian
go get -d -u github.com/google/battery-historian/...
go run setup.go
# cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go [--port <default:9999>]
具体步骤:
1、git clone https://github.com/google/battery-historian.git 或者直接下载后解压
2、进入battery-historian文件,打开终端
3、执行go get -d -u github.com/google/battery-historian/...
4、进入setup.go,更改版本为20190513,最稳定版本
5、开始编译,执行go run setup.go
6、启动:go run cmd/battery-historian/battery-historian.go [--port
测试步骤
使用batterystats生成数据
使用Battery historian分析数据
batterystats收集数据
1、清理耗电量数据
adb shell dumpsys batterystats --reset
adb shell dumpsys batterystats --enable full-wake-history
2、运行测试用例/手工操作
3、收集数据
Android 7.0:adb bugreport bugreport.zip
Android 6.0: adb bugreport>bugreport.txt
上传数据
打开localhost:9999,把zip或者txt数据上传
报告生成
Historian
x轴代表时间周期,默认以60s为一个周期
指标含义:
battery_level:电量
plugged:充电状态及充电时长
screen:屏幕是否点亮
top:显示当前手机运行的app
status:电池状态信息,有充电、放电、未充电、已充满,未知等不同状态
方式三、dumpsys
建议:https://developer.android.com/guide/background
弱网问题
封闭环境,网速降低
用于测试系统在出现故障时,是否能够自动恢复或者忽略故障继续运行。
操作过程
在正常情况和异常情况下(弱网,数据不同等)进行长时间操作 (通常使用monkey长时间操作)
建议使用第三方网站测试,比如阿里云移动端测试:https://www.aliyun.com/product/mqc?spm=5176.19720258.J_8058803260.444.1c852c4aIfPwku
不同硬件、软件或者硬件之间的配合度(手机设备,系统版本,浏览器版本,分辨率等)
APP兼容性测试
移动设备型号多样
测试APP在主流设备上能否正常运行
测试APP在主流设备奔溃卡顿现象
移动端APP运行在shell和Linux是相同的,所以我们查看APP性能,可以使用所有Linux查看性能的方法。
字段解释:r:排队数量b:阻塞数量swpd:虚拟内存free空闲内存buff缓冲cache缓存si虚拟内存换入so虚拟内存换出bi磁盘换入 bo磁盘换出 in中断次数 cs切换次数 us使用CPU时间 sy系统CPU时间 id空闲CPU时间 wa 等待IO CPU时间
打印对应应用实时信息
使用bash提取对应的指标
获取vmstat最后一行的第4个数
adb shell vmstat | tail -1 | awk '{print $4}'
while true;do adb shell vmstat | tail -1 | awk '{print $4}'; done
使用Python代码获取
def test_vmstat():
cmd = 'adb shell vmstat'
res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
# print(res.stdout.readline())
# 获取vmstat信息,获取的是b字节,使用str转码,转码后以"\r\n"切割,然后获取第3个数
print(str(res.stdout.read(),encoding="utf-8").split("\r\n")[2].split()[3])
作用:1、改善任何过程的一般原则 2、加速计算机系统估值
参数:
S:加速比
a: 系统某部分所需时间与总时间的比例
k:性能提升比例