开启掘金成长之旅!这是我参与「掘金日新计划 · 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支持不同的服务调用,那么如何实现的呢?
关注我,下篇分享