如今大部分的APP测试工作关注点主要集中在功能的逻辑与交互上,由于各种原因(比如测试时间不够,测试手段有限等等),对APP客户端的性能数据往往比较忽视,然而经过移动互联网爆发式发展后,许多App功能差别不大,同质化问题泛滥,这个时候APP的性能优劣很大程度的影响了用户体验,对用户的选择有着直接的影响。因此,在项目发展到一定阶段,用户量增长到较大的数量级后,对APP客户端的性能关注需要越发重视了。
目前,对于Android端的APP性能监控,GT、Emmagee等工具都具备针对某个应用的监控,通用性强,但缺少定制和灵活性。抱着试一试的心态,我尝试着自己来实现:
1.能够实时监控并展现 指定应用的CPU、PSS、流量、应用大小等数据;
2.能够在手工测试时配合使用,也能与自动化测试结合使用
3.能支持2个版本的数据比对
一、数据采集方法
1.CPU数据的采集
方法:使用adb shell执行top命令过滤包名查看进程,获取返回的数据,提取CPU占用率
adb shell "top -n 1 | grep com.xxxxx"
如有多个进程,需合并多个进程的数据.
示例代码:
String TOP_CPUINFO = "adb shell \"top -n 1 | grep " + packageName + "\"";
InputStream is = Runtime.getRuntime().exec(TOP_CPUINFO).getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String temp = "";
while ((temp = br.readLine()) != null) {
line += temp + "\n";
}
2.PSS 数据的采集
方法:使用adb shell执行dumpsys meminfo app进程查看内存信息,获取返回的数据,提取TOTAL PSS占用
adb shell "dumpsys meminfo com.xxxxxx|grep TOTAL"
示例代码:
String TOP_PSSINFO = "adb shell \"dumpsys meminfo " + packageName + "|grep TOTAL\" ";
InputStream is = Runtime.getRuntime().exec(TOP_PSSINFO).getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String temp = "";
while ((temp = br.readLine()) != null) {
line += temp + "\n";
}
Pss = Float.parseFloat(line.split("\\s+")[2]);
3.流量数据采集
方法:需要编写一个数据监控apk来完成,通过apk运行实时采集数据保存到文件,再使用adb shell读取文件的内容。Apk中通过Android SDK提供的网络类TrafficStats相应方法获取 应用发送和接受的总流量。
示例代码:
UID = getPackageManager().getApplicationInfo(AppPackageName, 0).uid;
/** 获取指定 UID 对应的应用程序通过所有网络方式收发的字节流量总数(包括 wifi) */
totalTraffic = TrafficStats.getUidRxBytes(UID) + TrafficStats.getUidTxBytes(UID);
// android 7.0 用以上的方法拿不到数据,只能从文件中拿 /proc/uid_stat/
if (totalTraffic == 0 || (TrafficStats.getUidRxBytes(UID) == -1) && (TrafficStats.getUidTxBytes(UID) == -1)) {
totalTraffic = getTotalBytesFromFile(UID);
}
//保留2位小数
Total = (float) (Math.round(totalTraffic / (float) (1024) * 100)) / 100;
//输出到文件
writeFileSdcard(AppTrafficFile, Total + " KB");
private Long getTotalBytesFromFile(int localUid){
File uidFileDir = new File("/proc/uid_stat/" + String.valueOf(localUid));
BufferedReader brReceived = new BufferedReader(new FileReader(new File(uidFileDir, "tcp_rcv")));
BufferedReader brSent = new BufferedReader(new FileReader(new File(uidFileDir, "tcp_snd")));
return Long.valueOf(brReceived).longValue() + Long.valueOf(brSent).longValue();
}
4.应用大小
方法:同流量的采集(利用数据监控apk来完成),通过apk运行实时采集数据保存到文件,再使用adb shell读取文件的内容。 安装包的大小信息封装在PackageStats类中,但在AndroidSDK中并没有显示提供方法来获得该对象,只能通过AIDL,然后利用Java的反射机制去调用该方法(getPackageSizeInfo)。
示例代码:
Method method = PackageManager.class.getMethod("getPackageSizeInfo",new Class[] { String.class, IPackageStatsObserver.class });
// 调用 getPackageSizeInfo 方法,需要两个参数:1、需要检测的应用包名;2、回调
method.invoke(context.getPackageManager(),
pkgName, new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException{
// 从pStats中提取各个所需数据
cachesize = Formatter.formatFileSize(context, pStats.cacheSize); //缓存大小
datasize = Formatter.formatFileSize(context, pStats.dataSize); //数据大小
codesize = Formatter.formatFileSize(context, pStats.codeSize); //应用程序大小
totalsize = Formatter.formatFileSize(context, pStats.cacheSize + pStats.dataSize + pStats.codeSize);
//输出到文件
writeFileSdcard(AppSizeFile, "缓存大小=" + cachesize + "\n数据大小=" + datasize + "\n程序大小=" + codesize + "\n总大小=" + totalsize);
}
});
需要的2个aidl文件,包名为:android.content.pm,工程结构:
2个aidl的内容:
IPackageStatusObserver.aidl文件
package android.content.pm;
import android.content.pm.PackageStats;
oneway interface IPackageStatsObserver{
void onGetStatsCompleted(in PackageStats pStats, boolean succeeded);
}
PackageStats.aidl文件
package android.content.pm;
parcelable PackageStats;
在manifest中需要添加权限:
二、使用方式
数据采集代码可导出为jar包,配置成jenkins任务随时调用,根据测试需要手动或自动触发。
三、展现效果
采集得到的数据通过js图表以网页形式展现,读取数据文件后使用Chart.js绘制曲线图,并支持2个数据文件的对比展示。
选择单次测试采集文件展现:
选择2次相同场景的测试采集文件对比展现:
本文来自网易实践者社区,经作者李琼授权发布。