Android端 APP性能监控实践

如今大部分的APP测试工作关注点主要集中在功能的逻辑与交互上,由于各种原因(比如测试时间不够,测试手段有限等等),对APP客户端的性能数据往往比较忽视,然而经过移动互联网爆发式发展后,许多App功能差别不大,同质化问题泛滥,这个时候APP的性能优劣很大程度的影响了用户体验,对用户的选择有着直接的影响。因此,在项目发展到一定阶段,用户量增长到较大的数量级后,对APP客户端的性能关注需要越发重视了。 

目前,对于Android端的APP性能监控,GTEmmagee 等工具都具备针对某个应用的监控,通用性强,但缺少定制和灵活性。抱着试一试的心态,我尝试着自己来实现:

1. 能够实时监控并展现 指定应用的CPUPSS、流量、应用大小等数据;

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);
            }
        });

需要的2aidl文件,包名为:android.content.pm,工程结构:

2aidl的内容:

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中需要添加权限:

<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"></uses-permission>


二、使用方式

数据采集代码可导出为jar包,配置成jenkins任务随时调用,根据测试需要手动或自动触发。


三、展现效果

采集得到的数据通过js图表以网页形式展现,读取数据文件后使用Chart.js绘制曲线图,并支持2个数据文件的对比展示。 

选择单次测试采集文件展现:


选择2次相同场景的测试采集文件对比展现:

本文来自网易实践者社区,经作者李琼授权发布。