方案
今天还是会借着启动流量自动化测试脚本来顺带讲解其中涉及的Linux命令,所以头图依旧没变
网上也有很多关于流量自动化测试的文章,但很多比较陈旧,都是基于Android4.3机器做的测试,最新版本的系统在读取流量的目录上发生了一些变化,所以在此解释一下,在 Android 4.3 以前,系统是通过读取 /proc/uid_stat/{uid} 文件来获取流量数据的,但在 Android 4.3 之后,就被 /proc/net/xt_qtaguid/stats 取代了。
在开始之前需要先准备一台root过的Android手机,这里建议大家用小米或者Google的Pixel系列手机去root比较容易,根据我的经验,做移动端的性能测试常备一台root机是非常有必要的,能给你带来很多方便。
这里我们需要从命令行传入三个参数:设备ID、启动activity、采样次数。
1、首先是根据启动activity获取包名:
packagename=$(echo $2 | cut -d "/" -f1)
首先需要了解脚本“位置参数”的概念,假设一个脚本在运行时可以接受参数,那么从左到右第一个参数被记作$1,第二个参数为$2,以此类推,第n个参数为$N。所有参数记作$@或$*,参数的总个数记作$#,而脚本本身记作$0。
上面的命令意思是把第二个参数打印出来传递给cut命令,顾名思义,cut就是截取的意思,它能处理的对象是“一行”文本,可从中选取出用户所需要的部分。在有特定的分隔符时,可以指定分隔符,然后打印出以分隔符隔开的具体某一列或某几列,这里cut的用法如下:
cut -d "分隔符" -f 指定的列
2、根据应用包名获取UID:
userid=$(adb -s $deviceid shell dumpsys package $packagename | grep userId= | sed 's/ //g' | cut -c 8-12)
由于adb dumpsys命令获取到的userId前面包含空格如下图:
这里可以用到sed命令,sed(stream editor)是一种非交互式的流编辑器,通过多种转换修改流经它的文本。但是请注意,默认情况下,sed并不会改变原文件本身,而只是对流经sed命令的文本进行修改,并将修改后的结果打印到标准输出中(也就是屏幕),通过使用s参数可将查找到的匹配文本内容替换为新的文本,s/旧文本/新文本/g,这里我们是变相把空格替换掉了,后面的g代表完成所有匹配值的替换。
接着把输出传递给cut,这里可以利用cut的-c参数打印指定列的字符,我们想要的是userid的值,于是传入8-12,截取10933。
3、循环采样
这里通过一个while循环来达到采样N次的效果:
counter=$3while [[ $counter -gt 0 ]]do command let "counter-=1"done
这个循环体比较简单,就是判断counter计数器的值是否大于0,如果是就继续循环,每次循环计数器的值减一,let是Shell内建的整数运算命令。
4、读取 /proc/net/xt_qtaguid/stats 的数据
这里通过adb命令读取
对应的表头的列名称和意思如下:
dx :序号 iface :代表流量类型(rmnet表示2G/3G, wlan表示Wifi流量,lo表示本地流量) acct_tag_hex :线程标记(用于区分单个应用内不同模块/线程的流量) uid_tag_int :应用uid,据此判断是否是某应用统计的流量数据 cnt_set :应用前后标志位:1:前台, 0:后台 rx_btyes :receive bytes 接受到的字节数 rx_packets :接收到的任务包数 tx_bytes :transmit bytes 发送的总字节数 tx_packets :发送的总包数 rx_tcp_types :接收到的tcp字节数 rx_tcp_packets :接收到的tcp包数 rx_udp_bytes :接收到的udp字节数 rx_udp_packets :接收到的udp包数 rx_other_bytes :接收到的其他类型字节数 rx_other_packets :接收到的其他类型包数 tx_tcp_bytes :发送的tcp字节数 tx_tcp_packets :发送的tcp包数 tx_udp_bytes :发送的udp字节数 tx_udp_packets :发送的udp包数 tx_other_bytes :发送的其他类型字节数 tx_other_packets :发送的其他类型包数
这里我们先获取指定UID的前台流量消耗情况:
startrx=$(adb -s $deviceid shell cat /proc/net/xt_qtaguid/stats | grep "$userid 1" | awk '{print $6}' | sed -n '1p')
这里我们需要的是第6、第8列 rx_bytes 和 tx_bytes ,通过awk可以很轻松打印出对应的列,后面的sed是用来打印指定列的第一行数据域,使用p命令可进行打印,这里使用sed命令时一定要加-n参数,表示不打印没关系的行。从之前的例子中可以看出,由于sed的工作原理是基于行的,因此每次都有大量的输出。可是这些输出中有一些是我们并不需要看到的,而只需要输出匹配的行或者处理过的行就好了。
最终的效果:
下面是完整的脚本:
#!/bin/bash # 设备ID deviceid=$1 # 启动activity activity=$2 # 采样次数 counter=$3 # 截取包名 packagename=$(echo $2 | cut -d "/" -f1) echo "Package name is '$packagename'" # 截取uid userid=$(adb -s $deviceid shell dumpsys package $packagename | grep userId= | sed 's/ //g' | cut -c 8-12) echo "uid = $userid" while [[ $counter -gt 0 ]] do # 停止应用的进程 adb -s $deviceid shell am force-stop $packagename # 清除应用数据与缓存 adb -s $deviceid shell pm clear $packagename startrx=$(adb -s $deviceid shell cat /proc/net/xt_qtaguid/stats | grep "$userid 1" | awk '{print $6}' | sed -n '1p') starttx=$(adb -s $deviceid shell cat /proc/net/xt_qtaguid/stats | grep "$userid 1" | awk '{print $8}' | sed -n '1p') echo "初始时接收的流量: $startrx bytes" echo "初始时发送的流量: $starttx bytes" # 启动应用 adb -s $deviceid shell am start -n $activity # 等待10s,应用启动后可能会加载一些数据资源 sleep 10s endrx=$(adb -s $deviceid shell cat /proc/net/xt_qtaguid/stats | grep "$userid 1" | awk '{print $6}' | sed -n '1p') endtx=$(adb -s $deviceid shell cat /proc/net/xt_qtaguid/stats | grep "$userid 1" | awk '{print $8}' | sed -n '1p') echo "结束时接收的流量: $endrx bytes" echo "结束时发送的流量: $endtx bytes" # 本次启动耗费的总流量 let "data=($endrx+$endtx)-($startrx+$starttx)" let "count=$3-$counter+1" echo "应用启动消耗的流量第 $count 次测试结果:$data bytes" let "sum+=$data" let "counter-=1" done let "averagedata=$sum/3" echo "=================================================" echo "应用启动的流量消耗 (采样次数:$3):$averagedata bytes"