1. 程式人生 > >Android系統CPU使用率獲取(附java程式碼)

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檔案內容,然後計算。