有时候我们想要知道当前手机的一些状态信息,可以使用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的一些变化,几乎可以忽略不计。如果对数据结果精度要求较高的话,不建议使用这种方式来获取。大家自行斟酌。