以一定的频率来监控app的cpu,内存,流量,电量等性能指标,输出到xls文件中。再通过xls软件即可方便的绘制出性能曲线,用于app客户端的性能测试。同时,该app支持对安装在手机里的app进行monkey测试,而不需要连接数据线通过adb工具来启动monkey测试。
程序实现的思路很简单
1、获取已安装的应用app list,排除系统应用,因为我们的目标是去测试各种应用app
2、上述app list中,选择一个待测试的app,即可进行手工测试,或者monkey测试,同时测试过程中自动采集相关的性能数据,cpu ,内存,流量,电量。
那么如何获取已安装应用的app list呢?
1、通过PackageManager的getInstalledApplications方法获取所有安装的app list
/**
* get information of all applications.
*
* @param context
* context of activity
* @return packages information of all applications
*/
private List getPackagesInfo(Context context) {
PackageManager pm = context.getApplicationContext().getPackageManager();
List appList = pm
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
return appList;
}
/**
* get information of all running processes,including package name ,process
* name ,icon ,pid and uid.
*
* @param context
* context of activity
* @return running processes list
*/
public List getRunningProcess(Context context) {
Log.i(LOG_TAG, "get running processes");
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List run = am.getRunningAppProcesses();
PackageManager pm = context.getPackageManager();
List progressList = new ArrayList();
boolean launchTag;
for (ApplicationInfo appinfo : getPackagesInfo(context)) {
launchTag = false;
Programe programe = new Programe();
if (((appinfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0)
|| ((appinfo.processName != null) && (appinfo.processName
.equals(PACKAGE_NAME)))) {
continue;
}
for (RunningAppProcessInfo runningProcess : run) {
if ((runningProcess.processName != null)
&& runningProcess.processName
.equals(appinfo.processName)) {
launchTag = true;
programe.setPid(runningProcess.pid);
programe.setUid(runningProcess.uid);
break;
}
}
programe.setPackageName(appinfo.processName);
programe.setProcessName(appinfo.loadLabel(pm).toString());
if (launchTag) {
programe.setIcon(appinfo.loadIcon(pm));
}
progressList.add(programe);
}
return progressList;
}
private class ListAdapter extends BaseAdapter {
List programe;
int tempPosition = -1;
/**
* save status of all installed processes
*
* @author andrewleo
*/
class Viewholder {
TextView txtAppName;
ImageView imgViAppIcon;
RadioButton rdoBtnApp;
}
public ListAdapter() {
programe = processInfo.getRunningProcess(getBaseContext());
}
@Override
public int getCount() {
return programe.size();
}
@Override
public Object getItem(int position) {
return programe.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
}
那么又如何进行性能数据采集呢?
这就需要用到service了。被测程序启动的时候,同时启动监测service,该service负责相关数据的采集。
电量的监控,service注册一个电量变化时间的额广播接收器即可。
流量的监控,android2.2以上的版本,可以直接调用TrafficStats这个类读取app相关的流量值,包括上下行,总流量等。低于android2.2版本的手机,则需要读取系统文件。这个要根据不同的手机来区别对待,因为碎片化的原因,很多手机存放cpu 内存 流量等值的文件并不是在一个特定的目录。各有不同。
cpu监控,均是通过读文件的方式进行获取.
某一具体进程的cpu占用,可以在这个目录读取,/proc/processPid /stat
整个系统的cpu占用,可以在这个目录读取,/proc/stat
/**
* read the status of CPU.
*
* @throws FileNotFoundException
*/
public void readCpuStat() {
Log.i(LOG_TAG,"READ CPU ,PID="+pid);
String processPid = Integer.toString(pid);
String cpuStatPath = "/proc/" + processPid + "/stat";
try {
// monitor cpu stat of certain process
RandomAccessFile processCpuInfo = new RandomAccessFile(cpuStatPath,
"r");
String line = "";
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.setLength(0);
while ((line = processCpuInfo.readLine()) != null) {
stringBuffer.append(line + "\n");
}
String[] tok = stringBuffer.toString().split(" ");
processCpu = Long.parseLong(tok[13]) + Long.parseLong(tok[14]);
processCpuInfo.close();
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "FileNotFoundException: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
// monitor total and idle cpu stat of certain process
RandomAccessFile cpuInfo = new RandomAccessFile("/proc/stat", "r");
String[] toks = cpuInfo.readLine().split("\\s+");
idleCpu = Long.parseLong(toks[4]);
totalCpu = Long.parseLong(toks[1]) + Long.parseLong(toks[2])
+ Long.parseLong(toks[3]) + Long.parseLong(toks[4])
+ Long.parseLong(toks[6]) + Long.parseLong(toks[5])
+ Long.parseLong(toks[7]);
cpuInfo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
内存的监控,可以直接调用ActivityManager的getProcessMemoryInfo方法,然后然会PSS值。
/**
* get the memory of process with certain pid.
*
* @param pid
* pid of process
* @param context
* context of certain activity
* @return memory usage of certain process
*/
public int getPidMemorySize(int pid, Context context) {
Log.i(LOG_TAG,"GET PSS MEM,PID="+pid);
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
int[] myMempid = new int[] { pid };
Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
memoryInfo[0].getTotalSharedDirty();
// int memSize = memoryInfo[0].dalvikPrivateDirty;
// TODO PSS
int memSize = memoryInfo[0].getTotalPss();
// int memSize = memoryInfo[0].getTotalPrivateDirty();
return memSize;
}
又如何做到定时采集呢?
直接在service启动的时候,调用一个timertask,或者用handler的postDelayed方法。其中task里面再次调用handler的postDelayed方法,即可做到定时采集。
private Runnable task = new Runnable() {
public void run() {
if (!isServiceStop) {
dataRefresh();
handler.postDelayed(this, delaytime);
} else {
Intent intent = new Intent();
intent.putExtra("isServiceStop", true);
intent.setAction("com.klaus.test.myservice");
sendBroadcast(intent);
stopSelf();
}
}
};
又是如何不通过数据线连接手机就可以执行monkey测试的呢?
直接在app中调用如下命令行即可
String cmd = "monkey -p "+packagename+" -s 98345678 --pct-touch "+touchevent+" --pct-motion "+motionevent+" --throttle 500 1000000";
当然需要在su模式下才能运行。
android自带的monkey测试,以前都是通过adb shell去驱动的,现在直接在手机里运行,不再通过adb桥接。
主线程UI,service,monkey线程,如何通信?
通过Handler机制进行通信,通过handler,三者可以进行通信。