Emmagee(一)-Cpu读取的实现分析

介绍


目前测试android性能的工具有很多,如腾讯的GT,网易的Emmagge以及安测试。其中网易的Emmagee是开源的,所以我们主要是来分析Emmagge的实现原理来确定软件获取到的数据的可靠性。这样子我们才能够放心的使用它。
Emmagee(机关枪)是网易杭州研究院QA团队开发的一个简单易上手的Android性能监测小工具,主要用于监控单个App的CPU,内存,流量,启动耗时,电量,电流等性能状态的变化,且用户可自定义配置监控的频率以及性能的实时显示,并最终生成一份性能统计文件。我们可以通过Emmagge 查看到对应的源代码。

这里写图片描述

通过点击开始测试即可跳转到对应的测试程序进行统计数据了。

原理


这次主要是分析下Emmagge获取cpu数据的原理。 首先在我们选中测试的应用程序点击开始测试时,Emmagge会通过ActivityManager获取到对应程序的包名,进程名等

ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//通过am的getRunningAppProcesses方法获取到运行的进程信息
List<RunningAppProcessInfo> run = am.getRunningAppProcesses();
PackageManager pm = context.getPackageManager();
List<Programe> progressList = new ArrayList<Programe>();
//遍历进程的列表将进程名,进程id赋值到programe对象上。
for (ApplicationInfo appinfo : getPackagesInfo(context)) {
    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)) {
            programe.setPid(runningProcess.pid);
            programe.setUid(runningProcess.uid);
            break;
        }
    }
    programe.setPackageName(appinfo.processName);
    programe.setProcessName(appinfo.loadLabel(pm).toString());
    programe.setIcon(appinfo.loadIcon(pm));
    progressList.add(programe);
}
Collections.sort(progressList);

通过比较我们选择的应用的包名以及后台运行的进程的包名来判断app是否已经正常运行起来了。现在我们就正式来看看获取cpu使用率吧,首先cpu使用率需要分为设备总的cpu使用率以及当前进程的cpu占用率,我们首先看看总的cpu的使用率:

try {
    // monitor total and idle cpu stat of certain process
    RandomAccessFile cpuInfo = new RandomAccessFile(CPU_STAT, "r");
    String line = "";
    while ((null != (line = cpuInfo.readLine())) && line.startsWith("cpu")) {
        String[] toks = line.split("\\s+");
        idleCpu.add(Long.parseLong(toks[4]));
        totalCpu.add(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();
}

在proc/stat下有详细的CPU使用情况,由于考虑到有多核的情况所以进行了while循环读取,但是实际看到后面发现,使用的数据只是使用了第一个总的cpu的时间就可以了.文件的详细格式如下:

CPU 152342 1421 28562 1600830 12389 553 273 0 0

CPU后面的几位数字分别是

  • user 从系统启动开始累计到当前时刻,处于用户态的运行时间,不包含 nice值为负进程。
  • nice 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间
  • system 从系统启动开始累计到当前时刻,处于核心态的运行时间
  • idle 从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间
  • iowait 从系统启动开始累计到当前时刻,IO等待时间
  • irq 从系统启动开始累计到当前时刻,硬中断时间
  • softirq 从系统启动开始累计到当前时刻,软中断时间

    所以CPU总数用率的算法是:100*((totalCpuTimeS-totalCpuTimeF) -(idelS-idelF))/ (totalCpuTimeS-totalCpuTimeF)

当前进程cpu的占用率

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

因为android设备对应的pid的CPU使用情况实在/proc/pid/stat路径下。并且文本的内容为

9978 (o.easicare.demo) S 152 152 0 0 -1 4202816 12650 0 0 0 70 68 0 0 ...

后面的四位分别代表的是

  • utime 该任务在用户运行状态的时间
  • stime 该任务在核心运行的时间
  • cutime 所有已死线程在用户状态运行状态的时间
  • cstime 所有已死线程在核心的运行时间

所以当前进程所占CPU的算法是:100*(processCpuTimeS-processCpuTimeF)/(totalCpuTimeS-totalCpuTimeF)

现在我们进入到真正计算cpu使用率的方法中

//这里首先判断上一次获取的数据是否有值,如果没有可能是第一次获取所以调到else中给totalCpu2复制
if (null != totalCpu2 && totalCpu2.size() > 0) {
     // (应用占用的cpu时间-上次应用占用cpu时间)/(设备总的cpu时间-上次设备总的cpu时间)
    processCpuRatio = fomart.format(100 * ((double) (processCpu - processCpu2) / ((double) (totalCpu.get(0) - totalCpu2.get(0)))));
    //以下是计算cpu的总的使用率 
    for (int i = 0; i < (totalCpu.size() > totalCpu2.size() ? totalCpu2.size() : totalCpu.size()); i++) {
        String cpuRatio = "0.00";
        if (totalCpu.get(i) - totalCpu2.get(i) > 0) {
            cpuRatio = fomart
                    .format(100 * ((double) ((totalCpu.get(i) - idleCpu.get(i)) - (totalCpu2.get(i) - idleCpu2.get(i))) / (double) (totalCpu
                            .get(i) - totalCpu2.get(i))));
        }
        totalCpuRatio.add(cpuRatio);
        totalCpuBuffer.append(cpuRatio + Constants.COMMA);
    }
} else {
    processCpuRatio = "0";
    totalCpuRatio.add("0");
    totalCpuBuffer.append("0,");
    totalCpu2 = (ArrayList<Long>) totalCpu.clone();
    processCpu2 = processCpu;
    idleCpu2 = (ArrayList<Long>) idleCpu.clone();
}

虽然上面程序都遍历计算了每个cpu的占用率,但是最后返回的仍然是第一个,也就是总的cpu时间。

cpuUsedRatio.add(processCpuRatio);
cpuUsedRatio.add(totalCpuRatio.get(0));

结论

Emmagee获取cpu的数据大体就是如此,后续会继续研究其他的cpu值是否有意义,如果没有回将这部分代码去掉。

参考

Android获取cpu使用率,剩余内存和硬盘容量

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值