騰訊效能監控框架Matrix原始碼分析(十七)SQLiteLint耗電分析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
@Override
public void onLooperTaskOverHeat(@NonNull List
@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`支援不同的服務呼叫,那麼如何實現的呢?
關注我,下篇分享