有上篇文章可知,配置的Task
会在BatteryMonitorCore
执行start
、stop
、onForeground
会执行onTurnOn
、onTurnOff
、onForeground
方法,有些Task
还会执行onBackgroundCheck
方法等。所以就从Task
的onTurnOn
、onTurnOff
、onForeground
、onBackgroundCheck
等这些方法来看相应的监控。
由配置Task的顺序来看
1.JiffiesMonitorFeature
该类主要由onForeground
来驱动,主要是对app在前后台对线程及进程的监控记录和比较。
private final ThreadWatchDog mFgThreadWatchDog = new ThreadWatchDog();
private final ThreadWatchDog mBgThreadWatchDog = new ThreadWatchDog();
public void onForeground(boolean isForeground) {
super.onForeground(isForeground);
if (isForeground) {
mFgThreadWatchDog.start();
mBgThreadWatchDog.stop();
} else {
mBgThreadWatchDog.start();
mFgThreadWatchDog.stop();
}
}
public void watchBackThreadSate(boolean isForeground, int pid, int tid) {
if (isForeground) {
// 给线程添加需要观察的线程
mFgThreadWatchDog.watch(pid, tid);
} else {
mBgThreadWatchDog.watch(pid, tid);
}
}
class ThreadWatchDog implements Runnable {
private long duringMillis;
// 观察的线程集合
private final List<ProcessInfo.ThreadInfo> mWatchingThreads = new ArrayList<>();
@Override
public void run() {
List<JiffiesSnapshot.ThreadJiffiesSnapshot> threadJiffiesList = new ArrayList<>();
synchronized (mWatchingThreads) {
for (ProcessInfo.ThreadInfo item : mWatchingThreads) {
// 解析 "/proc/" + pid + "/task/" + tid + "/stat" 里面的信息
// 拿到了 tid 线程的相关信息并封装到 ThreadJiffiesSnapshot 中,主要拿到了 tid 的名字、utime(用户态运行的时间)、
// stime(内核态运行的时间)、cutime (累计所有的waited-for进程曾经在用户态运行的时间)
// cstime (累计所有的waited-for进程曾经在内核态运行的时间)
JiffiesSnapshot.ThreadJiffiesSnapshot snapshot = JiffiesSnapshot.ThreadJiffiesSnapshot.parseThreadJiffies(item);
if (snapshot != null) {
snapshot.isNewAdded = false;
threadJiffiesList.add(snapshot);
}
}
}
if (!threadJiffiesList.isEmpty()) {
ListEntry<JiffiesSnapshot.ThreadJiffiesSnapshot> threadJiffiesListEntry = ListEntry.of(threadJiffiesList);// 包裹一层?
mCore.getConfig().callback.onWatchingThreads(threadJiffiesListEntry); // 主要做了 print
}
// 下一次运行维度时机,stop 方法则是将该任务移除
if (duringMillis <= 5 * 60 * 1000L) {
mCore.getHandler().postDelayed(this, setNext(5 * 60 * 1000L));
} else if (duringMillis <= 10 * 60 * 1000L) {
mCore.getHandler().postDelayed(this, setNext(10 * 60 * 1000L));
} else {
// done
synchronized (mWatchingThreads) {
mWatchingThreads.clear();
}
}
}
...
private long setNext(long millis) {
duringMillis += millis;
return millis;
}
}
此外还有一个函数需要注意
// 二个进程取差值 里面相同的线程也取差值
public Delta<JiffiesSnapshot> diff(JiffiesSnapshot bgn) {
return new Delta<JiffiesSnapshot>(bgn, this) {// bgn end
@Override
protected JiffiesSnapshot computeDelta() {
JiffiesSnapshot delta = new JiffiesSnapshot();
delta.pid = end.pid;
delta.name = end.name;
// 进程或者线程里面的 totalJiffies = utime + stime + cutime + cstime 的差值
delta.totalJiffies = Differ.DigitDiffer.globalDiff(bgn.totalJiffies, end.totalJiffies);
// 线程数差值
delta.threadNum = Differ.DigitDiffer.globalDiff(bgn.threadNum, end.threadNum);
delta.threadEntries = ListEntry.ofEmpty();
// 取二次进程快照中相同的线程做差值后并排序
if (end.threadEntries.getList().size() > 0) {
List<ThreadJiffiesSnapshot> deltaThreadEntries = new ArrayList<>();
for (ThreadJiffiesSnapshot endRecord : end.threadEntries.getList()) {
boolean isNewAdded = true;
long jiffiesConsumed = endRecord.value;
for (ThreadJiffiesSnapshot bgnRecord : bgn.threadEntries.getList()) {
if (bgnRecord.name.equals(endRecord.name) && bgnRecord.tid == endRecord.tid) {
isNewAdded = false;
jiffiesConsumed = Differ.DigitDiffer.globalDiff(bgnRecord, endRecord).value;
break;
}
}
if (jiffiesConsumed > 0) {
ThreadJiffiesSnapshot deltaThreadJiffies = new ThreadJiffiesSnapshot(jiffiesConsumed);
deltaThreadJiffies.tid = endRecord.tid;
deltaThreadJiffies.name = endRecord.name;
deltaThreadJiffies.stat = endRecord.stat;
deltaThreadJiffies.isNewAdded = isNewAdded;
deltaThreadEntries.add(deltaThreadJiffies);
}
}
if (deltaThreadEntries.size() > 0) {
Collections.sort(deltaThreadEntries, new Comparator<ThreadJiffiesSnapshot>() {
@Override
public int compare(ThreadJiffiesSnapshot o1, ThreadJiffiesSnapshot o2) {
long minus = o1.get() - o2.get();
if (minus == 0) return 0;
if (minus > 0) return -1;
return 1;
}
});
delta.threadEntries = ListEntry.of(deltaThreadEntries);
}
}
return delta;
}
};
}
- 总结:
- 取进程或者线程信息可以使用
proc/pid/stat
、/proc/pid/task/tid/stat
这二个命令。 - 进程或者线程中的 utime + stime + cutime + cstime 这几个值可以反应当前 cpu 状态,内核态时间过多可以表明不太正常等。
- 前后台监控的时机、间隔不一致
2.DeviceStatMonitorFeature
该类的启动是由onTurnOn
与onTurnOff
控制
@Override
public void onTurnOn() {
super.onTurnOn();
// 设备是否在 充电中/满电 熄屏 省电模式
int deviceStat = BatteryCanaryUtil.getDeviceStat(mCore.getContext());
@SuppressLint("VisibleForTests") TimeBreaker.Stamp firstStamp = new TimeBreaker.Stamp(String.valueOf(deviceStat));
synchronized (TAG) {
mStampList = new ArrayList<>();
// 记录最近一次设备的状态
mStampList.add(0, firstStamp);
}
mDevStatListener.setListener(new Consumer<Integer>() {
@SuppressLint("VisibleForTests")
@Override
// 该回调由前篇中 BatteryMonitorCore 类的 start 方法中的 BatteryEventDelegate.getInstance().attach(this).startListening(); 来触发
// 前篇代码 BatteryMonitorCore start 方法代码
// if (BatteryEventDelegate.isInit()) {
// // 通过广播来监听系统的 屏幕状态(息屏、亮屏),电池状态(充电、不充电),发生状态更改时会通知观察者们
// BatteryEventDelegate.getInstance().attach(this).startListening();
// }
public void accept(Integer integer) {
BatteryCanaryUtil.getProxy().updateDevStat(integer); // 更新 设备是否在 充电中/满电 熄屏 省电模式
synchronized (TAG) {
if (mStampList != Collections.EMPTY_LIST) {
MatrixLog.i(BatteryEventDelegate.TAG, "onStat >> " + BatteryCanaryUtil.convertDevStat(integer));
mStampList.add(0, new TimeBreaker.Stamp(String.valueOf(integer))); //设备状态记录
checkOverHeat(); // 判断 mStampList 数量是否达到阈值,释放 mStampList 一部分空间
}
}
}
});
if (!mDevStatListener.isListening()) {
mDevStatListener.startListen(mCore.getContext());
}
}
@Override
public void onTurnOff() {
super.onTurnOff();
// 移除监听
mDevStatListener.stopListen();
}
// 判断是否 充电中/满电
public static boolean isDeviceChargingV1(Context context) {
try {
Intent batIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (batIntent == null) return false;
int status = batIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);//电池状态
return (status == BatteryManager.BATTERY_STATUS_CHARGING) || (status == BatteryManager.BATTERY_STATUS_FULL);//充电中 或者 满电
} catch (Throwable ignored) {
return false;
}
}
// 判断是否是亮屏
public static boolean isDeviceScreenOn(Context context) {
try {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm != null) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? pm.isInteractive() : pm.isScreenOn();
}
} catch (Exception ignored) {
}
return false;
}
// 判断是否是开启省电模式
public static boolean isDeviceOnPowerSave(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm != null) {
return pm.isPowerSaveMode();
}
} catch (Exception ignored) {
}
}
return false;
}
而最终mStampList
记录的数据会在currentDevStatSnapshot
该方法中被使用
// 获取当前设备状态的快照
public DevStatSnapshot currentDevStatSnapshot() {
return currentDevStatSnapshot(0L);
}
public DevStatSnapshot currentDevStatSnapshot(long windowMillis) {
try {
// 返回 连接电源 断开电源 亮屏 熄屏 这几种状态的占比
TimeBreaker.TimePortions timePortions = TimeBreaker.configurePortions(mStampList, windowMillis, 10L, new TimeBreaker.Stamp.Stamper() {
@Override
public TimeBreaker.Stamp stamp(String key) {
//设备是否在 充电 熄屏 省电模式
int devStat = BatteryCanaryUtil.getDeviceStat(mCore.getContext());
return new TimeBreaker.Stamp(String.valueOf(devStat));
}
});
DevStatSnapshot snapshot = new DevStatSnapshot();
snapshot.setValid(timePortions.isValid());
snapshot.uptime = Snapshot.Entry.DigitEntry.of(timePortions.totalUptime);
snapshot.chargingRatio = Snapshot.Entry.DigitEntry.of((long) timePortions.getRatio("1"));// 连接电源的百分比
snapshot.unChargingRatio = Snapshot.Entry.DigitEntry.of((long) timePortions.getRatio("2"));// 断开电源的百分比
snapshot.screenOff = Snapshot.Entry.DigitEntry.of((long) timePortions.getRatio("3"));// 熄屏的百分比
snapshot.lowEnergyRatio = Snapshot.Entry.DigitEntry.of((long) timePortions.getRatio("4"));// 低电量的百分比
return snapshot;
} catch (Throwable e) {
MatrixLog.w(TAG, "configureSnapshot fail: " + e.getMessage());
DevStatSnapshot snapshot = new DevStatSnapshot();
snapshot.setValid(false);
return snapshot;
}
}
此外该类还可以监控CPU的频率和电池的温度
public CpuFreqSnapshot currentCpuFreq() {
CpuFreqSnapshot snapshot = new CpuFreqSnapshot();
try {
snapshot.cpuFreqs = Snapshot.Entry.ListEntry.ofDigits(BatteryCanaryUtil.getCpuCurrentFreq());
} catch (Throwable e) {
MatrixLog.printErrStackTrace(TAG, e, "#currentCpuFreq error");
snapshot.cpuFreqs = Snapshot.Entry.ListEntry.ofDigits(new int[]{});
}
return snapshot;
}
// 获得每隔 cpu 核心的频率
public static int[] getCpuCurrentFreq() {
int[] output = new int[getCpuCoreNum()];
for (int i = 0; i < getCpuCoreNum(); i++) {
output[i] = 0;
String path = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq";
String cat = cat(path);
if (!TextUtils.isEmpty(cat)) {
try {
//noinspection ConstantConditions
output[i] = Integer.parseInt(cat) / 1000;
} catch (Exception ignored) {
}
}
}
return output;
}
// 获取 cpu 核心数
public static int getCpuCoreNum() {
try {
// Get directory containing CPU info
File dir = new File("/sys/devices/system/cpu/");
// Filter to only list the devices we care about
File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return Pattern.matches("cpu[0-9]+", pathname.getName());
}
});
// Return the number of cores (virtual CPU devices)
return files.length;
} catch (Exception ignored) {
// Default to return 1 core
return 1;
}
}
public BatteryTmpSnapshot currentBatteryTemperature(Context context) {
BatteryTmpSnapshot snapshot = new BatteryTmpSnapshot();
snapshot.temp = Snapshot.Entry.DigitEntry.of(mCore.getCurrentBatteryTemperature(context));
return snapshot;
}
// 获取电池温度
public static int getBatteryTemperature(Context context) {
try {
Intent batIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (batIntent == null) return 0;
return batIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
} catch (Throwable ignored) {
return 0;
}
}
- 总结
- 获取电池温度:
Intent batIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (batIntent == null) return 0;
return batIntent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0);
- 获取 CPU 核心数:
"/sys/devices/system/cpu/"
- 获取单核 CPU 的频率:
"/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq"
- 获取设备状态:注册接收其接收系统发出的广播或者获取相应的系统服务来获取,代码见上方
3.AppStatMonitorFeature
在activity onCreate和 stop 时收集 app 是否在前台 有前台服务 在后台信息并记录,并且在 app 进入后台后(过指定时间后)根据判断 app 的前台服务的 importance 值来输出相应的信息。
@Override
public void onTurnOn() {
super.onTurnOn();
TimeBreaker.Stamp firstStamp = new TimeBreaker.Stamp("1");
TimeBreaker.Stamp firstSceneStamp = new TimeBreaker.Stamp(mCore.getScene()); // mCore.getScene() = Current AppScene
synchronized (TAG) {
mStampList = new ArrayList<>();
mStampList.add(0, firstStamp);
mSceneStampList = new ArrayList<>();
mSceneStampList.add(0, firstSceneStamp);
}
}
@Override
public void onTurnOff() {
super.onTurnOff();
synchronized (TAG) {
mStampList.clear();
mSceneStampList.clear();
}
}
@Override
// activity onCreate(isForeground = true) 和 stop(isForeground = false) 时回调
public void onForeground(boolean isForeground) {
super.onForeground(isForeground);
// 判断 app 是否在前台 有前台服务 在后台
int appStat = BatteryCanaryUtil.getAppStatImmediately(mCore.getContext(), isForeground);
BatteryCanaryUtil.getProxy().updateAppStat(appStat);
synchronized (TAG) {
if (mStampList != Collections.EMPTY_LIST) {
MatrixLog.i(BatteryEventDelegate.TAG, "onStat >> " + BatteryCanaryUtil.convertAppStat(appStat));
mStampList.add(0, new TimeBreaker.Stamp(String.valueOf(appStat)));
checkOverHeat();
}
}
MatrixLog.i(TAG, "updateAppImportance when app " + (isForeground ? "foreground" : "background"));
updateAppImportance();
}
// importance 值说明
//public static final int IMPORTANCE_BACKGROUND = 400//后台
//public static final int IMPORTANCE_EMPTY = 500//空进程
//public static final int IMPORTANCE_FOREGROUND = 100//在屏幕最前端、可获取到焦点
//public static final int IMPORTANCE_SERVICE = 300//在服务中
//public static final int IMPORTANCE_VISIBLE = 200//在屏幕前端、获取不到焦点
// 获取到所有的运行的程序,更新本进程组进程的 importance 值和本进程的 importance值
private void updateAppImportance() {
// 理论上不可能进入该 if
// 默认1024(本函数最后更新该值)<= 100 默认1024(本函数最后更新该值)<= 100
if (mAppImportance <= mForegroundServiceImportanceLimit && mGlobalAppImportance <= mForegroundServiceImportanceLimit) {
return;
}
Runnable runnable = new Runnable() {
@SuppressWarnings("SpellCheckingInspection")
@Override
public void run() {
Context context = mCore.getContext();
String mainProc = context.getPackageName();
if (mainProc.contains(":")) {
mainProc = mainProc.substring(0, mainProc.indexOf(":"));
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return;
}
//获取当前运行程序信息
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
if (processes == null) {
return;
}
for (ActivityManager.RunningAppProcessInfo item : processes) {
if (item.processName.startsWith(mainProc)) { // 同一个进程组
// default = 1024
if (mGlobalAppImportance > item.importance) {
MatrixLog.i(TAG, "update global importance: " + mGlobalAppImportance + " > " + item.importance
+ ", reason = " + item.importanceReasonComponent);
mGlobalAppImportance = item.importance;
}
if (item.processName.equals(context.getPackageName())) { // 主进程
// default = 1024
if (mAppImportance > item.importance) {
MatrixLog.i(TAG, "update app importance: " + mAppImportance + " > " + item.importance
+ ", reason = " + item.importanceReasonComponent);
mAppImportance = item.importance;
}
}
}
}
}
};
if (Looper.myLooper() == Looper.getMainLooper()) {
mCore.getHandler().post(runnable);
} else {
runnable.run();
}
}
@WorkerThread
@Override
// app stop 后台时回调 duringMillis = 10min
public void onBackgroundCheck(long duringMillis) {
super.onBackgroundCheck(duringMillis);
MatrixLog.i(TAG, "#onBackgroundCheck, during = " + duringMillis);
if (mGlobalAppImportance > mForegroundServiceImportanceLimit || mAppImportance > mForegroundServiceImportanceLimit) {
Context context = mCore.getContext();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return;
}
// 获取正在运行后台服务列表
List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(Integer.MAX_VALUE);
if (runningServices == null) {
return;
}
for (ActivityManager.RunningServiceInfo item : runningServices) {
// app 进程组的服务
if (!TextUtils.isEmpty(item.process) && item.process.startsWith(context.getPackageName())) {
if (item.foreground) {
MatrixLog.i(TAG, "checkForegroundService whether app importance is low, during = " + duringMillis);
// foreground service is running when app importance is low
if (mGlobalAppImportance > mForegroundServiceImportanceLimit) {
// global
MatrixLog.w(TAG, "foreground service detected with low global importance: "
+ mAppImportance + ", " + mGlobalAppImportance + ", " + item.service);
mCore.onForegroundServiceLeak(false, mAppImportance, mGlobalAppImportance, item.service, duringMillis);
}
if (mAppImportance > mForegroundServiceImportanceLimit) {
if (item.process.equals(context.getPackageName())) {
// myself
MatrixLog.w(TAG, "foreground service detected with low app importance: "
+ mAppImportance + ", " + mGlobalAppImportance + ", " + item.service);
mCore.onForegroundServiceLeak(true, mAppImportance, mGlobalAppImportance, item.service, duringMillis);
}
}
}
}
}
}
// MatrixLog.i(TAG, "checkBackgroundAppState when app background, during = " + duringMillis);
// checkBackgroundAppState(duringMillis);
}
- 总结:
- 可以根据进程信息中的 importance 来判断 app 的状态
- importance = 400 后台
- importance = 500 空进程
- importance =100 在屏幕最前端、可获取到焦点
- importance = 300 在服务中
- importance = 200 在屏幕前端、获取不到焦点
结束。余下几个是一类