Android開發之獲取手機硬體狀態資訊(CPU資訊/頻率/使用率、DDR頻率/使用率、電池瞬時電流/電壓/庫倫counter)
阿新 • • 發佈:2018-12-21
有時候我們想要知道當前手機的一些狀態資訊,可以使用app(root 或者系統簽名 )來顯示獲取。
OK,接下來看一下一些關鍵的程式碼。
我這裡使用的是高通的手機,不同硬體平臺的機型,其獲取資訊的節點可能不一樣。
/** * 獲取當前瞬時電流 * @return 返回獲取的電流 */ public String getCurrent() { String[] cmds = {"cat /sys/class/power_supply/battery/current_now"}; ShellUtils.CommandResult rs = ShellUtils.execCommand(cmds, false, true); if (!TextUtils.isEmpty(rs.successMsg)) { return "當前電流:"+rs.successMsg; } else { return "當前電流:" + "unknown"; } } /** * 獲取當前Counter * @return 獲取的counter */ public String getCounter() { String[] cmds = new String[]{"cat /sys/class/power_supply/battery/charge_counter"}; ShellUtils.CommandResult rs = ShellUtils.execCommand(cmds, false, true); if (!TextUtils.isEmpty(rs.successMsg)) { return "庫倫值:" + rs.successMsg; } else { return "庫倫值:" + "unknown"; } //Log.e("debug","庫倫值:"+rs.successMsg); } /** * 獲取GPU的頻率 * @return 返回GPU頻率 */ public String getGPU() { String[] cmds = new String[]{"cat /sys/class/kgsl/kgsl-3d0/gpuclk"}; ShellUtils.CommandResult rs = ShellUtils.execCommand(cmds, false, true); if (!TextUtils.isEmpty(rs.successMsg)) { return "GPU:" + rs.successMsg+"("+gpuBusy()+")"; } else { return "GPU:" + "unknown"+"("+gpuBusy()+")"; } } /** * DDR的頻率和使用率 * @return */ public String clkMeasure() { String[] cmds = new String[]{"cd /d/clk/bimc_clk/","cat clk_measure"}; ShellUtils.CommandResult rs = ShellUtils.execCommand( cmds, false, true); // Log.e("xxxxx",rs.errorMsg); if (!TextUtils.isEmpty(rs.successMsg)) { return "DDR:" + rs.successMsg+"("+meminfo()+")"; } else { return "DDR:" + "unknown"+"("+meminfo()+")"; } } /** * 獲取當前DDR使用率 * @return 返回記憶體使用率% */ public String meminfo() { String[] cmds = new String[]{"cat proc/meminfo"}; ShellUtils.CommandResult rs = ShellUtils.execCommand(cmds, false, true); if (!TextUtils.isEmpty(rs.successMsg)) { String[] tmpList = rs.successMsg.split("kB"); Double total = null, free = null, available = null, cached = null; for (String line : tmpList) { if (line.contains("MemTotal:")) { total = Double.valueOf(Pattern.compile("[^0-9]").matcher(line).replaceAll("")); } if (line.contains("MemFree:")) { free = Double.valueOf(Pattern.compile("[^0-9]").matcher(line).replaceAll("")); } if (line.contains("MemAvailable:")) { available = Double.valueOf(Pattern.compile("[^0-9]").matcher(line).replaceAll("")); } if (line.contains("Cached:")) { cached = Double.valueOf(Pattern.compile("[^0-9]").matcher(line).replaceAll("")); if (total != null && total > 0 && free != null && free > 0) { break; } } } available = 0.00; return String.valueOf(100-mathRound((free + cached) / total * 100)) + "%"; //Log.e("debug","DDR使用率:"+String.valueOf((total-free-available)/total*100)+"%"); } else { return "unknown"; } } /** * CPU使用率、狀態、頻率 * @return */ private String getCPUStatus() { String cpuStatusString = ""; String cpuOnlineStatus = ""; String cpuFreqStatus = ""; ArrayList<OneCpuInfo> currentInfo = takeCpuUsageSnapshot(); int[] cpuUsages = calcCpuUsages(currentInfo, mLastInfo); if (cpuUsages != null && cpuUsages.length > 0) { cpuStatusString = "CPU狀態:"+cpuUsages[0] + "%\n"; // Log.d(TAG, "cpuUsages length=" + cpuUsages.length); } else { cpuStatusString = "CPU狀態:"+"unknown\n"; //Log.d(TAG, "cpuUsages is null"); } int cpuNum = getCpuNum(); int onlineCPUCount = 1; for (int i=0; i<cpuNum; i++) { ShellUtils.CommandResult rs = ShellUtils.execCommand( new String[]{"cat /sys/devices/system/cpu/cpu" + i + "/online"}, false, true); cpuOnlineStatus = rs.successMsg; if (cpuOnlineStatus.contains("1")) { rs = ShellUtils.execCommand( new String[]{"cat sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq"}, false, true); cpuFreqStatus = rs.successMsg; if (cpuFreqStatus == null || cpuFreqStatus.length() == 1) { cpuStatusString += "cpu" + i + "0 MHz"; } else { cpuFreqStatus = cpuFreqStatus.substring(0, cpuFreqStatus.length() - 3); if (cpuUsages != null && onlineCPUCount < cpuUsages.length) { cpuStatusString += "cpu" + i + " " + cpuFreqStatus + " MHz ("+ cpuUsages[onlineCPUCount] + "%)"; } else { if (cpuUsages != null) Log.d(TAG, "onlineCPUCount=" + onlineCPUCount + " cpuUsages.length=" + cpuUsages.length); cpuStatusString += "cpu" + i + " " + cpuFreqStatus + " MHz (0%)"; } } onlineCPUCount++; } else { cpuStatusString += "cpu" + i + " offline"; } if (i<cpuNum-1) { cpuStatusString = cpuStatusString + "\n"; } } mLastInfo = currentInfo; return cpuStatusString; }
工具類ShellUtils :
package com.meitutest.getstat; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.List; public class ShellUtils { public static final String COMMAND_SU = "su"; public static final String COMMAND_SH = "sh"; public static final String COMMAND_EXIT = "exit\n"; public static final String COMMAND_LINE_END = "\n"; /** * execute shell commands, default return result msg * * @param commands command list * @param isRoot whether need to run with root * @return * @see ShellUtils#execCommand(String[], boolean, boolean) */ public static CommandResult execCommand(List<String> commands, boolean isRoot) { return execCommand(commands == null ? null : commands.toArray(new String[]{}), isRoot, true); } /** * execute shell commands * * @param commands command array * @param isRoot whether need to run with root * @param isNeedResultMsg whether need result msg * @return <ul> * <li>if isNeedResultMsg is false, {@link CommandResult#successMsg} is null and {@link CommandResult#errorMsg} is * null.</li> * <li>if {@link CommandResult#result} is -1, there maybe some excepiton.</li> * </ul> */ public static CommandResult execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg) { int result = -1; if (commands == null || commands.length == 0) { return new CommandResult(result, null, null); } Process process = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; DataOutputStream os = null; try { process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); os = new DataOutputStream(process.getOutputStream()); for (String command : commands) { if (command == null) { continue; } if (command.contains("uiautomator")) {// 只有執行指令碼的時候需要清除快取 StreamGobbler ssgError = new StreamGobbler(process.getErrorStream(), "Error"); StreamGobbler ssgOutput = new StreamGobbler(process.getInputStream(), "Output"); ssgError.start(); ssgOutput.start(); } os.write(command.getBytes()); os.writeBytes(COMMAND_LINE_END); os.flush(); } os.writeBytes(COMMAND_EXIT); os.flush(); result = process.waitFor(); if (isNeedResultMsg) { successMsg = new StringBuilder(); errorMsg = new StringBuilder(); successResult = new BufferedReader(new InputStreamReader(process.getInputStream()), 1024); errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream())); String s; while ((s = successResult.readLine()) != null) { successMsg.append(s); } while ((s = errorResult.readLine()) != null) { errorMsg.append(s); } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (os != null) { os.close(); } if (successResult != null) { successResult.close(); } if (errorResult != null) { errorResult.close(); } } catch (IOException e) { e.printStackTrace(); } if (process != null) { process.destroy(); } } return new CommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null : errorMsg.toString()); } /** * execute shell commands * * @param commands command array * @param isRoot whether need to run with root * @param isNeedResultMsg whether need result msg * @return <ul> * <li>if isNeedResultMsg is false, {@link CommandResult#successMsg} is null and {@link CommandResult#errorMsg} is * null.</li> * <li>if {@link CommandResult#result} is -1, there maybe some excepiton.</li> * </ul> */ public static CommandResult execCommandWithNewLine(String[] commands, boolean isRoot, boolean isNeedResultMsg) { int result = -1; if (commands == null || commands.length == 0) { return new CommandResult(result, null, null); } Process process = null; BufferedReader successResult = null; BufferedReader errorResult = null; StringBuilder successMsg = null; StringBuilder errorMsg = null; DataOutputStream os = null; try { process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH); os = new DataOutputStream(process.getOutputStream()); for (String command : commands) { if (command == null) { continue; } if (command.contains("uiautomator")) {// 只有執行指令碼的時候需要清除快取 StreamGobbler ssgError = new StreamGobbler(process.getErrorStream(), "Error"); StreamGobbler ssgOutput = new StreamGobbler(process.getInputStream(), "Output"); ssgError.start(); ssgOutput.start(); } os.write(command.getBytes()); os.writeBytes(COMMAND_LINE_END); os.flush(); } os.writeBytes(COMMAND_EXIT); os.flush(); result = process.waitFor(); if (isNeedResultMsg) { successMsg = new StringBuilder(); errorMsg = new StringBuilder(); successResult = new BufferedReader(new InputStreamReader(process.getInputStream()), 1024); errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream())); String s; while ((s = successResult.readLine()) != null) { successMsg.append(s+"\n"); } while ((s = errorResult.readLine()) != null) { errorMsg.append(s+"\n"); } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (os != null) { os.close(); } if (successResult != null) { successResult.close(); } if (errorResult != null) { errorResult.close(); } } catch (IOException e) { e.printStackTrace(); } if (process != null) { process.destroy(); } } return new CommandResult(result, successMsg == null ? null : successMsg.toString(), errorMsg == null ? null : errorMsg.toString()); } /** * result of command * <ul> * <li>{@link CommandResult#result} means result of command, 0 means normal, else means error, same to excute in * linux shell</li> * <li>{@link CommandResult#successMsg} means success message of command result</li> * <li>{@link CommandResult#errorMsg} means error message of command result</li> * </ul> * * @author <a href="http://www.trinea.cn" target="_blank">Trinea</a> 2013-5-16 */ public static class CommandResult { /** * result of command **/ public int result; /** * success message of command result **/ public String successMsg; /** * error message of command result **/ public String errorMsg; public CommandResult(int result, String successMsg, String errorMsg) { this.result = result; this.successMsg = successMsg; this.errorMsg = errorMsg; } } }
工具類Testreport:
package com.meitutest.getstat; import java.io.File; import java.io.FileOutputStream; import java.util.Calendar; import java.util.TimeZone; public class TestReport { public static final int OPEN = 0; public static final int INFO = 2; public static final int ERROR = 3; public static final int FAIL = 4; public static final int SUCCESS = 5; private static final int SELF_DEFINE_ONLY_TXT = 1; private static final int SELF_DEFINE = 6; public static final int CRASH = 1000; public static int logfile_switch = OPEN; public static int logcat_switch = OPEN; public static int errors, failures, success; private static String log_path = "/sdcard/GetSystemStat/";// Environment.getExternalStorageDirectory().getPath(); private static String log_default_tag = "autotest"; public static int i(String msg) { return i(log_default_tag, msg); } public static int i(String tag, String msg) { return println(INFO, tag, msg); } public static int e(String msg) { return e(log_default_tag, msg); } public static int e(String tag, String msg) { errors++; return println(ERROR, tag, msg); } public static String getLogPath() { return log_path; } public static boolean checkLogPath(String newPath) { if (newPath == null) { return false; } boolean ret = false; try { File logPath = new File(newPath); if (!logPath.exists()) { logPath.mkdirs(); } if (logPath.isDirectory()) { ret = true; } else { ret = false; } } catch (Throwable tr) { ret = false; } return ret; } private static int printToFile(int priority, String tag, String type, byte[] buffer, int offset, int count) { int ret = 0; String logpath = getLogPath(); if (priority == CRASH) { type = "crash"; } if (priority < logfile_switch || !checkLogPath(logpath)) { if (priority == CRASH) { logpath = "/sdcard/"; type = "crash"; if (!checkLogPath(logpath)) { return ret; } } else { return ret; } } Thread thread = Thread.currentThread(); // String threadInfo = "id:"+thread.getId()+";name:"+thread.getName(); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+08:00")); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH) + 1; int day = cal.get(Calendar.DAY_OF_MONTH); int hour = cal.get(Calendar.HOUR_OF_DAY); int minute = cal.get(Calendar.MINUTE); int second = cal.get(Calendar.SECOND); int millisecond = cal.get(Calendar.MILLISECOND); String timeString = String.format("%d-%02d-%02d %02d:%02d:%02d.%d", year, month, day, hour, minute, second, millisecond); String headString = String.format("\r\n%s\t(%d)\ttag:%s\tdata:", timeString, priority, tag); byte[] headBuffer = headString.getBytes(); String fileName; switch (priority) { case INFO: fileName = "%s/AnLog_Info%d%02d%02d.%s"; break; case ERROR: fileName = "%s/AnLog_Error%d%02d%02d.%s"; break; case FAIL: fileName = "%s/AnLog_Fail%d%02d%02d.%s"; break; case SUCCESS: fileName = "%s/AnLog_Success%d%02d%02d.%s"; break; case SELF_DEFINE_ONLY_TXT: fileName = "%s/" + tag +".txt"; break; case SELF_DEFINE: fileName = "%s/" + tag +".txt"; break; default: fileName = "%s/%d%02d%02d.%s"; } fileName = String.format(fileName, logpath, year, month, day, type); FileOutputStream fo = null; try { File file = new File(fileName); fo = new FileOutputStream(file, true); fo.write(headBuffer); fo.write(buffer, offset, count); ret = headBuffer.length + count; } catch (Throwable tr) { ret = 0; } finally { if (fo != null) { try { fo.close(); } catch (Throwable tr) { } } } return ret; } public static int printMsgToFile(int priority, String tag, String msg) { if (msg == null) { msg = "[null]"; } byte[] buffer = msg.getBytes(); // if (tag != null && tag.startsWith("autotest")) { // printToFile(priority, tag, "autotest", buffer, 0, buffer.length); // } return printToFile(priority, tag, "txt", buffer, 0, buffer.length); } private static int println(int priority, String tag, String msg) { int ret = 0; if (priority >= logcat_switch) { ret += android.util.Log.println(priority, tag, msg); } if (priority >= logfile_switch) { ret += printMsgToFile(priority, tag, msg); } return ret; } public static int selfDefine(String tag, String msg, boolean showOnLog) { if (!showOnLog) { return println(SELF_DEFINE_ONLY_TXT, tag, msg); } else { return println(SELF_DEFINE, tag, msg); } } public static int selfDefine(String tag, String msg) { return selfDefine(tag, msg, false); } }
工具類StreamGobbler:
package com.meitutest.getstat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
public class StreamGobbler extends Thread {
InputStream is;
String type;
OutputStream os;
StreamGobbler(InputStream is, String type) {
this(is, type, null);
}
StreamGobbler(InputStream is, String type, OutputStream redirect) {
this.is = is;
this.type = type;
this.os = redirect;
}
public void run() {
try {
PrintWriter pw = null;
if (os != null)
pw = new PrintWriter(os);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null) {
if (pw != null)
pw.println(line);
System.out.println(type + ">" + line);
}
if (pw != null)
pw.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
新建一個執行緒,再新增使用一個TimerTask的子類,外加一個輸出視窗,讓這些方法間隔一段時間執行一次,便可以獲取實時獲取了。
我這裡還添加了一個持久化到本地的txt檔案中,便於後續查詢。
@Override
public void run() {
isRun = true;
// TestReport.selfDefine(TAG, getCurrent());
for (int i = 0; i < 9; i++) {
//當前電流
TestReport.selfDefine(TAG, getCurrent());
sleep(1000);
if (i == 8||i<100) {
// if (i < 9) {
String result="=======================\n";
//當前電流
result = result+getCurrent()+"uA\n";
//電流庫倫值
result = result+getCounter()+"uA\n";
//GPU頻率、使用率
result = result+getGPU()+"\n";
//DDR頻率、使用率
result = result+clkMeasure()+"\n";
//CPU資訊
result = result+getCPUStatus()+"\n";
Log.e("GetSystemState",result);
TestReport.selfDefine(TAG, result);
}
}
}
private static int println(int priority, String tag, String msg) {
int ret = 0;
if (priority >= logcat_switch) {
ret += android.util.Log.println(priority, tag, msg);
}
if (priority >= logfile_switch) {
ret += printMsgToFile(priority, tag, msg);
}
return ret;
}
public static int selfDefine(String tag, String msg, boolean showOnLog) {
if (!showOnLog) {
return println(SELF_DEFINE_ONLY_TXT, tag, msg);
} else {
return println(SELF_DEFINE, tag, msg);
}
}
public static int selfDefine(String tag, String msg) {
return selfDefine(tag, msg, false);
}
成果圖:
需要注意的是: 通過APP獲取並且做了一些資料上的處理,以及持久化到sdcard上。這些操作都會使用到CPU的處理、IO的讀寫等,可能會對獲取的資料非常輕微的影響。這個,大家可以自主權衡。我親測過電流和CPU的一些變化,幾乎可以忽略不計。如果對資料結果精度要求較高的話,不建議使用這種方式來獲取。大家自行斟酌。