Android系统CPU使用率获取(附java代码)

若想直接看有效方法,请阅读方法二,第一条。

 

最近因为一个需求,需要记录下当前的CPU使用率,在翻遍了API后,发现系统并没有给予一个方法,能够简单的获取相关CPU信息,没办法,只能自己写一个了。

在网上查阅了相关方法后,获取CPU使用率主要有两种方法。一个是利用adb top命令;另一个就是读取/proc/stat文件,然后解析相关参数,自己去计算。


方法一、解析top命令结果

这是执行adb shell top命令后的结果。


从图中可知,top命令的执行结果显示,在第一个非空白行上有各部分的CPU占用率,我们只要稍微整理下就行。

先上代码:

 

然而得到的结果显然不对,以下是从输入流中打印出来的命令执行结果:


显然,系统对于这个top命令动态的进行了修改,防止信息被App获取。正确的执行权限只留给了adbshell。

至此,此方法失效。

 

方法二、解析/proc/stat文件

/proc/stat文件动态记录所有CPU活动的信息,该文件中的所有值都是从系统启动开始累计到当前时刻。所以,计算系统当前的CPU占用率的方法就是,计算在间隔较短(ms级)的时间内,cpu的各活动信息的变化量,作为当前的实时CPU占用率。

下图是执行shell命令,获取文件的内容。


其中,以CPU开头的两行表示的信息就是,当前该CPI的一个总的使用情况,后面各个数值的单位为jiffies,可以简单理解为Linux中操作系统进程调度的最小时间片。具体含义如下(以CPU0为例):

user(181596)从系统启动开始累计到当前时刻,处于用户态的运行时间,不包含 nice值为负进程。;

nice(85733)从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间;

system (197165)从系统启动开始累计到当前时刻,处于核心态的运行时间;

idle (1328127)从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间;

iowait(11679)从系统启动开始累计到当前时刻,IO等待时间;

irq (5)从系统启动开始累计到当前时刻,硬中断时间;

softirq (5138)从系统启动开始累计到当前时刻,软中断时间。

 

这里,我们只需关心“idle”,它表示了系统的空闲时间,以及各项数值之和就是CPU的总消耗。

因此,我们以totalJiffies1表示第一次CPU总消耗,totalIdle1表示第一次的CPU空闲时间,同理,totalJiffies2、totalIdle2表示第二次的相关信息,则cpu的占用率如下:


double cpuRate=1.0*((totalIdle2-totalJiffies2)-(totalIdle1-totalJiffies1))/( totalIdle2- totalIdle1);

 

以下是根据这个思想实现的代码:

/**获取当前CPU占比
 * 在实际测试中发现,有的手机会隐藏CPU状态,不会完全显示所有CPU信息,例如MX5,所有建议只做参考
 * @return
 */
public static String getCPURateDesc(){
    String path = "/proc/stat";// 系统CPU信息文件
    long totalJiffies[]=new long[2];
    long totalIdle[]=new long[2];
    int firstCPUNum=0;//设置这个参数,这要是防止两次读取文件获知的CPU数量不同,导致不能计算。这里统一以第一次的CPU数量为基准
    FileReader fileReader = null;
    BufferedReader bufferedReader = null;
    Pattern pattern=Pattern.compile(" [0-9]+");
    for(int i=0;i<2;i++) {
        totalJiffies[i]=0;
        totalIdle[i]=0;
        try {
            fileReader = new FileReader(path);
            bufferedReader = new BufferedReader(fileReader, 8192);
            int currentCPUNum=0;
            String str;
            while ((str = bufferedReader.readLine()) != null&&(i==0||currentCPUNum<firstCPUNum)) {
                if (str.toLowerCase().startsWith("cpu")) {
                    currentCPUNum++;
                    int index = 0;
                    Matcher matcher = pattern.matcher(str);
                    while (matcher.find()) {
                        try {
                            long tempJiffies = Long.parseLong(matcher.group(0).trim());
                            totalJiffies[i] += tempJiffies;
                            if (index == 3) {//空闲时间为该行第4条栏目
                                totalIdle[i] += tempJiffies;
                            }
                            index++;
                        } catch (NumberFormatException e) {
                            e.printStackTrace();
                        }
                    }
                }
                if(i==0){
                    firstCPUNum=currentCPUNum;
                    try {//暂停50毫秒,等待系统更新信息。
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    double rate=-1;
    if (totalJiffies[0]>0&&totalJiffies[1]>0&&totalJiffies[0]!=totalJiffies[1]){
        rate=1.0*((totalJiffies[1]-totalIdle[1])-(totalJiffies[0]-totalIdle[0]))/(totalJiffies[1]-totalJiffies[0]);
    }

    return String.format("cpu:%.2f",rate);
}


思考:上述方法中,为了获取两次CPU信息,强制线程休眠了50ms,若实际文件内容的修改要比这个时间多怎么办?这样两次读取的信息就相同了;如果少于50ms,则白等了这么久。因此引入了FileObserve,当文件被修改时,系统回调相应的方法通知我们去读取新的内容,这样就可以避免上述问题。

 

方法很简单,就不全贴了,就附上变化的部分:

1、继承FileObserver类,并实现onEvent(int event, String path)方法,在执行该类的startWatching()方法后,系统会回调onEvent方法,告知该文件发生变化的情况,当判定为文件被修改时,就唤醒读取文件的线程,继续工作。


2、当第一次读取到文件输入流后,启动文件监听。


3、当第一次读取解析文件结束后,不再是执行sleep方法,而是执行wait方法,等待接收到系统的回调。


 

结果:第二次文件读取一直未进行。

通过日志发现,接收到的文件变化事件只有三类:


即:

 

即,文件只发生过访问数据、打开文件、关闭文件这三种操作。

这明显不可能,文件的内容一直在动态变化着!

 

通过上网查找资料发现:/proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为内核与进程提供通信的接口。用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取/proc目录中的文件时,proc文件系统是动态从系统内核读出所需信息并提交的。

 

也就是说,这是一个特殊的文件,是收不到文件修改事件的,想通过文件观察对象进行监督文件的修改情况的方法也就失效了。

 

总结:获取CPU使用率信息,目前相对最可靠的方式就是两次获取并/proc/stat文件内容,然后计算。

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以通过以下代码获取Android设备的CPU使用率: 1. 首先,你需要获取设备当前的CPU使用情况: ```java private static final String TAG = "CpuUsage"; private static final int UPDATE_INTERVAL = 1000; // 更新间隔时间 private static long lastTotalCpuTime = 0; private static long lastProcessCpuTime = 0; public static float getCpuUsage() { float cpuUsage = 0.0f; long totalCpuTime = getTotalCpuTime(); long processCpuTime = getProcessCpuTime(); if (lastTotalCpuTime != 0) { long deltaTotalCpuTime = totalCpuTime - lastTotalCpuTime; long deltaProcessCpuTime = processCpuTime - lastProcessCpuTime; cpuUsage = deltaProcessCpuTime * 100.0f / deltaTotalCpuTime; } lastTotalCpuTime = totalCpuTime; lastProcessCpuTime = processCpuTime; return cpuUsage; } private static long getTotalCpuTime() { try { BufferedReader reader = new BufferedReader(new FileReader("/proc/stat")); String line; while ((line = reader.readLine()) != null) { if (line.startsWith("cpu ")) { String[] fields = line.split("\\s+"); long totalCpuTime = 0; for (int i = 1; i < fields.length; i++) { totalCpuTime += Long.parseLong(fields[i]); } return totalCpuTime; } } } catch (IOException e) { Log.e(TAG, "Failed to read /proc/stat", e); } return 0; } private static long getProcessCpuTime() { try { int pid = android.os.Process.myPid(); BufferedReader reader = new BufferedReader(new FileReader("/proc/" + pid + "/stat")); String line = reader.readLine(); String[] fields = line.split("\\s+"); long utime = Long.parseLong(fields[13]); long stime = Long.parseLong(fields[14]); long cutime = Long.parseLong(fields[15]); long cstime = Long.parseLong(fields[16]); return utime + stime + cutime + cstime; } catch (IOException e) { Log.e(TAG, "Failed to read /proc/pid/stat", e); } return 0; } ``` 2. 然后,你需要使用一个定时器来定时获取CPU使用情况: ```java private Timer mTimer = new Timer(); private TimerTask mTimerTask = new TimerTask() { @Override public void run() { float cpuUsage = getCpuUsage(); Log.d(TAG, "CPU Usage: " + cpuUsage); } }; // 在onCreate()中启动定时器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTimer.schedule(mTimerTask, 0, UPDATE_INTERVAL); } // 在onDestroy()中停止定时器 @Override protected void onDestroy() { super.onDestroy(); mTimer.cancel(); } ``` 这样,你就可以通过代码获取Android设备的CPU使用率了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值