敞开生长之旅!这是我参与「日新方案 12 月更文应战」的第2天,点击查看活动概况
BatteryMonitorPlugin
好了,经过上一篇文章《Android App 电量计算原理与优化》的剖析,相信你现已彻底把握了 Android App 电量的计算办法,现在能够开始给自己的 App 开发电量反常检测功能了。
跟其他 APM 指标优化结构相同,电量优化结构 BatteryCanary 也是作为一个相对独立的 Plugin 功能集成在 Matrix 结构里边。不过电量相关的问题比较复杂,一般来说,比较 “客观” 的反常检测(比方 Crash 或者 ANR),都能做到 “开箱即用”(out-of-box);而像卡顿反馈(没有 ANR 弹窗但用户感觉卡顿)这类比较 “片面” 的反常检测,就需求做一些额定的自定义装备了。
电量反常的判断标准则要比卡顿问题 “片面更多”,并且导致耗电的原因更是多种多样,比方线程问题、WakeLock 问题、Wifi / 蓝牙 / GPS 扫描频繁问题等等。相应的 BatteryCanary 也开发了许多针对以上种种问题的功能模块,从而导致 BatteryCanary 的规划比较其他 APM 结构繁琐了不少,运用前需求根据自己 App 需求指定启用哪些模块以及相应的自定义装备。
初始化
首先在App的进口初始化装备,MatrixApplication onCreate
// Configure battery canary.
BatteryMonitorPlugin batteryMonitorPlugin = configureBatteryCanary();
builder.plugin(batteryMonitorPlugin);
Matrix.init(builder.build());
生成的代码较多,委托给了BatteryCanaryInitHelper
private BatteryMonitorPlugin configureBatteryCanary() {
// Configuration of battery plugin is really complicated.
// See it in BatteryCanaryInitHelper.
return BatteryCanaryInitHelper.createMonitor();
}
电量的计算支撑十分多的装备和回调监控
public static BatteryMonitorPlugin createMonitor() {
if (sBatteryConfig != null) {
throw new IllegalStateException("Duplicated init!");
}
sBatteryConfig = new BatteryMonitorConfig.Builder()
// Thread Activities Monitor
.enable(JiffiesMonitorFeature.class)
.enableStatPidProc(true)
.greyJiffiesTime(3 * 1000L)
.enableBackgroundMode(false)
.backgroundLoopCheckTime(30 * 60 * 1000L)
.enableForegroundMode(true)
.foregroundLoopCheckTime(20 * 60 * 1000L)
.setBgThreadWatchingLimit(5000)
.setBgThreadWatchingLimit(8000)
// App & Device Status Monitor For Better Invalid Battery Activities Configure
.setOverHeatCount(1024)
.enable(DeviceStatMonitorFeature.class)
.enable(AppStatMonitorFeature.class)
.setSceneSupplier(new Callable<String>() {
@Override
public String call() {
return "Current AppScene";
}
})
// AMS Activities Monitor:
// alarm/wakelock watch
.enableAmsHook(true)
.enable(AlarmMonitorFeature.class)
.enable(WakeLockMonitorFeature.class)
.wakelockTimeout(2 * 60 * 1000L)
.wakelockWarnCount(3)
.addWakeLockWhiteList("Ignore WakeLock TAG1")
.addWakeLockWhiteList("Ignore WakeLock TAG2")
// scanning watch (wifi/gps/bluetooth)
.enable(WifiMonitorFeature.class)
.enable(LocationMonitorFeature.class)
.enable(BlueToothMonitorFeature.class)
// .enable(NotificationMonitorFeature.class)
// Lab Feature:
// network monitor
// looper task monitor
.enable(TrafficMonitorFeature.class)
.enable(LooperTaskMonitorFeature.class)
.addLooperWatchList("main")
.useThreadClock(false)
.enableAggressive(true)
// Monitor Callback
.setCallback(new BatteryMonitorCallback() {
@Override
public void onTraceBegin() {
}
@Override
public void onTraceEnd(boolean isForeground) {
}
@Override
public void onAlarmDuplicated(int duplicatedCount, AlarmMonitorFeature.AlarmRecord record) {
}
@Override
public void onForegroundServiceLeak(boolean isMyself, int appImportance, int globalAppImportance, ComponentName componentName, long millis) {
}
@Override
public void onAppSateLeak(boolean isMyself, int appImportance, ComponentName componentName, long millis) {
}
@Override
public void onReportInternalJiffies(Delta<AbsTaskMonitorFeature.TaskJiffiesSnapshot> delta) {
}
@Override
public void onParseError(int pid, int tid) {
}
@Override
public void onWatchingThreads(MonitorFeature.Snapshot.Entry.ListEntry<? extends JiffiesSnapshot.ThreadJiffiesEntry> threadJiffiesList) {
}
@Override
public void onTaskTrace(Thread thread, List<LooperTaskMonitorFeature.TaskTraceInfo> sortList) {
}
@Override
public void onLooperTaskOverHeat(@NonNull List<Delta<AbsTaskMonitorFeature.TaskJiffiesSnapshot>> deltas) {
}
@Override
public void onLooperConcurrentOverHeat(String key, int concurrentCount, long duringMillis) {
}
@Override
public void onNotify(@NonNull NotificationMonitorFeature.BadNotification notify) {
}
@Override
public void onWakeLockTimeout(int warningCount, WakeLockRecord record) {
}
@Override
public void onWakeLockTimeout(WakeLockRecord record, long backgroundMillis) {
}
})
.build();
return new BatteryMonitorPlugin(sBatteryConfig);
}
注册好监控以后就能够开始监控了
Plugin plugin = Matrix.with().getPluginByClass(BatteryMonitorPlugin.class);
if (!plugin.isPluginStarted()) {
if (!BatteryEventDelegate.isInit()) {
BatteryEventDelegate.init(this.getApplication());
}
MatrixLog.i(TAG, "plugin-battery start");
plugin.start();
}
进入BatteryMonitorPlugin start
办法
public class BatteryMonitorPlugin extends Plugin {
private static final String TAG = "Matrix.battery.BatteryMonitorPlugin";
final BatteryMonitorCore mDelegate;
private static String sPackageName = null;
private static String sProcessName = null;
public BatteryMonitorPlugin(BatteryMonitorConfig config) {
mDelegate = new BatteryMonitorCore(config);
MatrixLog.i(TAG, "setUp battery monitor plugin with configs: " + config);
}
public BatteryMonitorCore core() {
return mDelegate;
}
@Override
public void init(Application app, PluginListener listener) {
super.init(app, listener);
if (!mDelegate.getConfig().isBuiltinForegroundNotifyEnabled) {
AppActiveMatrixDelegate.INSTANCE.removeListener(this);
}
}
@Override
public String getTag() {
return "BatteryMonitorPlugin";
}
@Override
public void start() {
super.start();
mDelegate.start();
}
@Override
public void stop() {
super.stop();
mDelegate.stop();
}
真实的执行者其实是BatteryMonitorCore
BatteryMonitorCore
com.tencent.matrix.batterycanary.monitor.BatteryMonitorCore
首先初始化相关参数,然后开始遍历MonitorFeature 的configure办法
public BatteryMonitorCore(BatteryMonitorConfig config) {
mConfig = config;
if (config.callback instanceof BatteryMonitorCallback.BatteryPrinter) ((BatteryMonitorCallback.BatteryPrinter) config.callback).attach(this);
if (config.onSceneSupplier != null) {
mSupplier = config.onSceneSupplier;
}
mHandler = new Handler(MatrixHandlerThread.getDefaultHandlerThread().getLooper(), this);
enableForegroundLoopCheck(config.isForegroundModeEnabled);
enableBackgroundLoopCheck(config.isBackgroundModeEnabled);
mMonitorDelayMillis = config.greyTime;
mFgLooperMillis = config.foregroundLoopCheckTime;
mBgLooperMillis = config.backgroundLoopCheckTime;
for (MonitorFeature plugin : config.features) {
plugin.configure(this);
}
}
再看看start stop其实也是遍历各个feature
public void start() {
synchronized (BatteryMonitorCore.class) {
if (!mTurnOn) {
for (MonitorFeature plugin : mConfig.features) {
plugin.onTurnOn();
}
mTurnOn = true;
}
if (BatteryEventDelegate.isInit()) {
BatteryEventDelegate.getInstance().attach(this).startListening();
}
}
}
public void stop() {
synchronized (BatteryMonitorCore.class) {
if (mTurnOn) {
mHandler.removeCallbacksAndMessages(null);
for (MonitorFeature plugin : mConfig.features) {
plugin.onTurnOff();
}
mTurnOn = false;
}
}
}
因为电量涉及到不同的模块,所以通过feature分门别类,进一步解耦,动态实现可插拔,以及支撑咱们自定义的扩展,秒啊!这些features就是咱们初始化装备增加进去的,咱们看看有哪些feature.
**见名知意 **涉及到Location时刻, Alarm次数,Net、WIFi、蓝牙访问量,wake_lock 持有时刻,CPU 耗电状况等,looper,app解锁屏,是否充电状况,告诉运用状况等等
当然BatteryMonitorCore 中心类的姓名不是白叫的,除了启动各个监控模块,自己还在记载前后信息以及告诉其他模块当时app生命周期信息
private class ForegroundLoopCheckTask implements Runnable {
int lastWhat = MSG_ID_JIFFIES_START;
@Override
public void run() {
if (mForegroundModeEnabled) {
Message message = Message.obtain(mHandler);
message.what = lastWhat;
message.arg1 = MSG_ARG_FOREGROUND;
mHandler.sendMessageAtFrontOfQueue(message);
lastWhat = (lastWhat == MSG_ID_JIFFIES_END ? MSG_ID_JIFFIES_START : MSG_ID_JIFFIES_END);
mHandler.postDelayed(this, mFgLooperMillis);
}
}
}
private class BackgroundLoopCheckTask implements Runnable {
int round = 0;
@Override
public void run() {
round++;
MatrixLog.i(TAG, "#onBackgroundLoopCheck, round = " + round);
if (!isForeground()) {
synchronized (BatteryMonitorCore.class) {
for (MonitorFeature plugin : mConfig.features) {
plugin.onBackgroundCheck(mBgLooperMillis * round);
}
}
}
if (!isForeground()) {
mHandler.postDelayed(this, mBgLooperMillis);
}
}
}
前后台切换时记载信息
public void onForeground(boolean isForeground) {
if (!Matrix.isInstalled()) {
MatrixLog.e(TAG, "Matrix was not installed yet, just ignore the event");
return;
}
mAppForeground = isForeground;
if (BatteryEventDelegate.isInit()) {
BatteryEventDelegate.getInstance().onForeground(isForeground);
}
if (!isForeground) {
// back:
// 1. remove all checks
mHandler.removeCallbacksAndMessages(null);
// 2. start background jiffies check
Message message = Message.obtain(mHandler);
message.what = MSG_ID_JIFFIES_START;
mHandler.sendMessageDelayed(message, mMonitorDelayMillis);
// 3. start background loop check task
if (mBackgroundModeEnabled) {
if (mBgLooperTask != null) {
mHandler.removeCallbacks(mBgLooperTask);
mBgLooperTask = null;
}
mBgLooperTask = new BackgroundLoopCheckTask();
mHandler.postDelayed(mBgLooperTask, mBgLooperMillis);
}
} else if (!mHandler.hasMessages(MSG_ID_JIFFIES_START)) {
// fore:
// 1. remove background loop task
if (mBgLooperTask != null) {
mHandler.removeCallbacks(mBgLooperTask);
mBgLooperTask = null;
}
// 2. finish background jiffies check
Message message = Message.obtain(mHandler);
message.what = MSG_ID_JIFFIES_END;
mHandler.sendMessageAtFrontOfQueue(message);
// 3. start foreground jiffies loop check
if (mForegroundModeEnabled && mFgLooperTask != null) {
mHandler.removeCallbacks(mFgLooperTask);
mFgLooperTask.lastWhat = MSG_ID_JIFFIES_START;
mHandler.post(mFgLooperTask);
}
}
for (MonitorFeature plugin : mConfig.features) {
plugin.onForeground(isForeground);
}
}
接收不同的模块的真实的回调 比方闹钟,告诉,app状况等等
public class BatteryMonitorCore implements
LooperTaskMonitorFeature.LooperTaskListener,
WakeLockMonitorFeature.WakeLockListener,
AlarmMonitorFeature.AlarmListener,
JiffiesMonitorFeature.JiffiesListener,
AppStatMonitorFeature.AppStatListener,
NotificationMonitorFeature.NotificationListener,
Handler.Callback {
private void notifyTraceBegin() {
MatrixLog.d(TAG, "#onTraceBegin");
getConfig().callback.onTraceBegin();
}
private void notifyTraceEnd(boolean isForeground) {
MatrixLog.d(TAG, "#onTraceEnd");
getConfig().callback.onTraceEnd(isForeground);
}
@Override
public void onTaskTrace(Thread thread, List<LooperTaskMonitorFeature.TaskTraceInfo> sortList) {
MatrixLog.d(TAG, "#onTaskTrace, thread = " + thread.getName());
getConfig().callback.onTaskTrace(thread, sortList);
}
@Override
public void onLooperTaskOverHeat(@NonNull List<Delta<TaskJiffiesSnapshot>> deltas) {
getConfig().callback.onLooperTaskOverHeat(deltas);
}
@Override
public void onLooperConcurrentOverHeat(String key, int concurrentCount, long duringMillis) {
getConfig().callback.onLooperConcurrentOverHeat(key, concurrentCount, duringMillis);
}
@Override
public void onWakeLockTimeout(int warningCount, WakeLockRecord record) {
getConfig().callback.onWakeLockTimeout(warningCount, record);
}
@Override
public void onWakeLockTimeout(WakeLockRecord record, long backgroundMillis) {
getConfig().callback.onWakeLockTimeout(record, backgroundMillis);
}
@Override
public void onAlarmDuplicated(int duplicatedCount, AlarmMonitorFeature.AlarmRecord record) {
getConfig().callback.onAlarmDuplicated(duplicatedCount, record);
}
@Override
public void onParseError(int pid, int tid) {
getConfig().callback.onParseError(pid, tid);
}
@Override
public void onWatchingThreads(ListEntry<? extends ThreadJiffiesEntry> threadJiffiesList) {
getConfig().callback.onWatchingThreads(threadJiffiesList);
}
@Override
public void onForegroundServiceLeak(boolean isMyself, int appImportance, int globalAppImportance, ComponentName componentName, long millis) {
getConfig().callback.onForegroundServiceLeak(isMyself, appImportance, globalAppImportance, componentName, millis);
}
@Override
public void onAppSateLeak(boolean isMyself, int appImportance, ComponentName componentName, long millis) {
getConfig().callback.onAppSateLeak(isMyself, appImportance, componentName, millis);
}
@Override
public void onNotify(BadNotification notification) {
getConfig().callback.onNotify(notification);
}
由次咱们知道BatteryMonitorCore
是电量监控的中枢大脑,负责各个模块的运作以及各种监控成果的回调和处理。
Feature
接下来咱们看看feature的规划,先进入上层接口 包括基础的开关,前后装备生命周期以及信息的记载抽象类Snapshot
public interface MonitorFeature {
void configure(BatteryMonitorCore monitor);
void onTurnOn();
void onTurnOff();
void onForeground(boolean isForeground);
void onBackgroundCheck(long duringMillis);
int weight();
@SuppressWarnings("rawtypes")
abstract class Snapshot<RECORD extends Snapshot> {
接下来进入实现接口的抽象类,做了相关日志的输出
public abstract class AbsMonitorFeature implements MonitorFeature {
private static final String TAG = "Matrix.battery.MonitorFeature";
protected String getTag() {
return TAG;
}
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
protected BatteryMonitorCore mCore;
@CallSuper
@Override
public void configure(BatteryMonitorCore monitor) {
MatrixLog.i(getTag(), "#configure");
this.mCore = monitor;
}
@CallSuper
@Override
public void onTurnOn() {
MatrixLog.i(getTag(), "#onTurnOn");
}
@CallSuper
@Override
public void onTurnOff() {
MatrixLog.i(getTag(), "#onTurnOff");
}
@CallSuper
@Override
public void onForeground(boolean isForeground) {
MatrixLog.i(getTag(), "#onForeground, foreground = " + isForeground);
}
@CallSuper
@WorkerThread
@Override
public void onBackgroundCheck(long duringMillis) {
MatrixLog.i(getTag(), "#onBackgroundCheck, since background started millis = " + duringMillis);
}
protected boolean shouldTracing() {
if (mCore.getConfig().isAggressiveMode) return true;
return 0 != (mCore.getContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE);
}
@Override
public String toString() {
return getTag();
}
}
再看看实现类,就是咱们真实监控的三大模块了
对于大部分模块的监控需求hook systemservice,这一类的代码都十分类似,咱们以WifiMonitorFeature
剖析为例
WifiMonitorFeature
onTurnOn是启动的进口 这儿hook了 wifi onStartScan
onGetScanResults
办法,并记载下相关信息,回调的收集比较简单,中心的逻辑在于WifiManagerServiceHooker.addListener(mListener)是怎么hook的?咱们进入看看
public final class WifiMonitorFeature extends AbsMonitorFeature {
private static final String TAG = "Matrix.battery.WifiMonitorFeature";
final WifiTracing mTracing = new WifiTracing();
WifiManagerServiceHooker.IListener mListener;
@Override
protected String getTag() {
return TAG;
}
@Override
public void onTurnOn() {
super.onTurnOn();
if (mCore.getConfig().isAmsHookEnabled) {
mListener = new WifiManagerServiceHooker.IListener() {
@Override
public void onStartScan() {
String stack = shouldTracing() ? BatteryCanaryUtil.stackTraceToString(new Throwable().getStackTrace()) : "";
MatrixLog.i(TAG, "#onStartScan, stack = " + stack);
mTracing.setStack(stack);
mTracing.onStartScan();
}
@Override
public void onGetScanResults() {
String stack = shouldTracing() ? BatteryCanaryUtil.stackTraceToString(new Throwable().getStackTrace()) : "";
MatrixLog.i(TAG, "#onGetScanResults, stack = " + stack);
mTracing.setStack(stack);
mTracing.onGetScanResults();
}
};
WifiManagerServiceHooker.addListener(mListener);
}
}
@Override
public void onTurnOff() {
super.onTurnOff();
WifiManagerServiceHooker.removeListener(mListener);
mTracing.onClear();
}
@Override
public int weight() {
return Integer.MIN_VALUE;
}
@NonNull
public WifiTracing getTracing() {
return mTracing;
}
public WifiSnapshot currentSnapshot() {
return mTracing.getSnapshot();
}
public static final class WifiTracing {
private int mScanCount;
private int mQueryCount;
private String mLastConfiguredStack = "";
public void setStack(String stack) {
if (!TextUtils.isEmpty(stack)) {
mLastConfiguredStack = stack;
}
}
public void onStartScan() {
mScanCount++;
}
public void onGetScanResults() {
mQueryCount++;
}
public void onClear() {
mScanCount = 0;
mQueryCount = 0;
}
public WifiSnapshot getSnapshot() {
WifiSnapshot snapshot = new WifiSnapshot();
snapshot.scanCount = Snapshot.Entry.DigitEntry.of(mScanCount);
snapshot.queryCount = Snapshot.Entry.DigitEntry.of(mQueryCount);
snapshot.stack = mLastConfiguredStack;
return snapshot;
}
}
public static class WifiSnapshot extends Snapshot<WifiSnapshot> {
public Entry.DigitEntry<Integer> scanCount;
public Entry.DigitEntry<Integer> queryCount;
public String stack;
@Override
public Delta<WifiSnapshot> diff(WifiSnapshot bgn) {
return new Delta<WifiSnapshot>(bgn, this) {
@Override
protected WifiSnapshot computeDelta() {
WifiSnapshot snapshot = new WifiSnapshot();
snapshot.scanCount = Differ.DigitDiffer.globalDiff(bgn.scanCount, end.scanCount);
snapshot.queryCount = Differ.DigitDiffer.globalDiff(bgn.queryCount, end.queryCount);
snapshot.stack = end.stack;
return snapshot;
}
};
}
}
}
WifiManagerServiceHooker
增加监听,包括去重逻辑
public synchronized static void addListener(IListener listener) {
if (listener == null) {
return;
}
if (sListeners.contains(listener)) {
return;
}
sListeners.add(listener);
//真实的hook点
checkHook();
}
查看并hook
private static void checkHook() {
if (sTryHook) {
return;
}
if (sListeners.isEmpty()) {
return;
}
boolean hookRet = sHookHelper.doHook();
MatrixLog.i(TAG, "checkHook hookRet:%b", hookRet);
sTryHook = true;
}
真实的执行者其实是sHookHelper,作为WifiManagerServiceHooker的变量现已提前生成了
private static SystemServiceBinderHooker.HookCallback sHookCallback = new SystemServiceBinderHooker.HookCallback() {
@Override
public void onServiceMethodInvoke(Method method, Object[] args) {
if ("startScan".equals(method.getName())) {
dispatchStartScan();
} else if ("getScanResults".equals(method.getName())) {
dispatchGetScanResults();
}
}
@Nullable
@Override
public Object onServiceMethodIntercept(Object receiver, Method method, Object[] args) {
return null;
}
};
private static SystemServiceBinderHooker sHookHelper = new SystemServiceBinderHooker(Context.WIFI_SERVICE, "android.net.wifi.IWifiManager", sHookCallback);
sHookCallback就是咱们hook体系服务后的回调,这儿做了一下中转。因为hook体系服务基于的原理都是相同的,无非就是动态署理,反射,安卓获取的体系服务大都彻底相同,所以这儿专门封装了SystemServiceBinderHooker
支撑不同的服务调用,那么怎么实现的呢?
关注我,下篇共享
阅读之前首先你需求把握 反射 动态署理 hook 能够回忆我的文章有具体的阐明