敞开生长之旅!这是我参与「日新方案 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.

腾讯性能监控框架Matrix源码分析(十七)SQLiteLint耗电分析2之架构设计

**见名知意 **涉及到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();
    }
}

再看看实现类,就是咱们真实监控的三大模块了

腾讯性能监控框架Matrix源码分析(十七)SQLiteLint耗电分析2之架构设计

对于大部分模块的监控需求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 能够回忆我的文章有具体的阐明