likes
comments
collection
share

Android性能优化系列-腾讯matrix-电量优化之BatteryMonitorPlugin源码分析

作者站长头像
站长
· 阅读数 25

前言

今天我们进入电量监控分析,matrix中电量监控相关的逻辑在BatteryMonitorPlugin类中。电量监控是一个很宽泛的概念,因为系统从启动之后,就在不停的消耗手机的电量,有些电量消耗是合理的,不需要过多关注,但有些电量消耗是异常的,这也是电量监控的目的,发现异常的电量消耗,从而进行优化,提升用户电池续航能力。

我们进入今天的代码分析,分析的对象是matrix中的BatteryMonitorPlugin,我们从它的几个关键方法入手。

  • 构造方法
  • start
  • stop

构造方法

在构造BatteryMonitorPlugin的时候,会传递一个BatteryMonitorConfig,这个config除了包含一些常规的配置之外,还有一个关键点,就是它的enable方法,enable方法会传入一个Class<? extends MonitorFeature>类型的class对象,并将它保存在features数组中。

public Builder enable(Class<? extends MonitorFeature> pluginClass) {
    try {
        config.features.add(pluginClass.newInstance());
    } catch (Exception ignored) {
    }
    return this;
}

matrix中定义了很多的feature,我们看看都有哪些。

  • MonitorFeature 接口
  • AbsMonitorFeature 抽象类
  • BatteryStatsFeature 用于记录电池温度,电池剩余电量等信息
  • WifiMonitorFeature 用于记录wifi相关的操作
  • BlueToothMonitorFeature 用于记录蓝牙相关的操作
  • WakeLockMonitorFeature 用于记录亮屏相关的操作
  • TrafficMonitorFeature 用于记录流量相关的操作
  • NotificationMonitorFeature 用于记录通知相关的操作
  • LooperTaskMonitorFeature 用于记录主线程消息队列相关的操作
  • LocationMonitorFeature 用于记录定位相关的操作
  • JiffiesMonitorFeature 用于记录jiffies相关的操作
  • InternalMonitorFeature 用于记录内部状态相关的操作
  • DeviceStatMonitorFeature 用于记录设备状态相关的操作
  • CpuStatFeature 用于记录cpu状态相关的操作
  • AppStatMonitorFeature 用于记录app状态相关的操作
  • AlarmMonitorFeature 用于记录闹钟相关的操作
  • CompositeMonitors 非AbsMonitorFeature子类,但是也比较关键

这里定义了所有有matrix框架认为的电量消耗大户,针对每一种耗电操作设计了一个feature类作为监听,这也是电量监控比其他类型的监控更复杂的地方,因为它涉及到的点非常多,后边我们会专项分析这些feature类,这里先有一个印象。

config配置完成,传入到BatteryMonitorPlugin的构造方法,构造方法中创建了一个BatteryMonitorCore对象,BatteryMonitorPlugin的核心实现,是通过这个对象完成的。

public BatteryMonitorPlugin(BatteryMonitorConfig config) {
    mDelegate = new BatteryMonitorCore(config);
    MatrixLog.i(TAG, "setUp battery monitor plugin with configs: " + config);
}

看一下它的构造方法,除了接收配置初始化配置之外,通过一个循环将所有的feature类进行了configure调用,将BatteryMonitorCore传递到每一个feature类中,于是feature持有了BatteryMonitorCore的引用。

@SuppressLint("VisibleForTests")
public BatteryMonitorCore(BatteryMonitorConfig config) {
    for (MonitorFeature plugin : config.features) {
        plugin.configure(this);
    }
}

start

构造方法调用完成,接下来就要启动BatteryMonitorPlugin,我们继续来看start方法,调用的还是BatteryMonitorCore的start。

@Override
public void start() {
    super.start();
    mDelegate.start();
}

进入BatteryMonitorCore的start方法,这里有两个操作,首先启动所有的feature对象,调用它们的onTurnOn方法,然后通过BatteryEventDelegate开启监听,今天我们优先分析第二步,feature相关的操作太多,后边会通过单独的文章进行分析,现在我们只需要知道,所有的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();
        }
    }
}

startListening

广播注册

startListening主要是注册广播接收者,并监听对应的广播。方法很长,我们分步来看。 首先注册广播,我们关注一下它监听了哪些内容,见注释。

IntentFilter filter = new IntentFilter();
//电池状态改变的广播,电池温度改变等信息
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//屏幕点亮
filter.addAction(Intent.ACTION_SCREEN_ON);
//屏幕熄灭
filter.addAction(Intent.ACTION_SCREEN_OFF);
//开始充电
filter.addAction(Intent.ACTION_POWER_CONNECTED);
//从充电到断电
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//电池已经从低电量恢复为正常
filter.addAction(Intent.ACTION_BATTERY_OKAY);
//电池已经从正常变为低电量状态
filter.addAction(Intent.ACTION_BATTERY_LOW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //省电模式
    filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        //低电耗模式(跟省电模式类似)
        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
    }
}

mContext.registerReceiver(new BroadcastReceiver());

ACTION_BATTERY_CHANGED监听

首先是频率的限制,每一分钟监听一次

if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
    long currMs = System.currentTimeMillis();
    if (mLastBatteryChangedHandleMs > 0 && currMs - mLastBatteryChangedHandleMs < ONE_MIN) {
        limited = true;
    }
    if (!limited) {
        ...
    }
}

当满足条件时,进入子线程执行下面的逻辑。

mCore.getHandler().post(new Runnable() {
    @Override
    public void run() {
        // 拿到电量剩余百分比
        int currPct = BatteryCanaryUtil.getBatteryPercentage(mContext);
        if (currPct >= 0 && currPct <= 1000) {
            //当电量比上次检测变化超过BATTERY_POWER_GRADUATION(5)时,达到记录的条件
            if (Math.abs(currPct - mLastBatteryPowerPct) >= BATTERY_POWER_GRADUATION) {
                //注意这里,从core中获取BatteryStatsFeature的信息,
                //BatteryStatsFeature记录的就是电量信息,拿到电量值,设置到
                //BatteryStatsFeature中,外界可以通过拿到BatteryStatsFeature
                //对象获取到实时变化的电量值信息
                BatteryStatsFeature feat = mCore.getMonitorFeature(BatteryStatsFeature.class);
                if (feat != null) {
                    feat.statsBatteryEvent(currPct);
                }
            }
        }
        // 拿到电池温度信息
        int currTemp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
        if (currTemp >= 0 && currPct <= 1000) {
            //温度变化超过15,就开始记录温度
            if (Math.abs(currTemp - mLastBatteryTemp) >= BATTERY_TEMPERATURE_GRADUATION) {
                //从mCore中拿到BatteryStatsFeature对象,将温度信息设置进去,
                //外界可以通过拿到BatteryStatsFeature对象获取到实时变化的温度信息
                BatteryStatsFeature feat = mCore.getMonitorFeature(BatteryStatsFeature.class);
                if (feat != null) {
                    feat.statsBatteryTempEvent(currTemp);
                }
            }
        }
    }
});

ps:获取电量信息的方式是通过注册一个接受者为空的广播,此时会直接返回一个intent,从intent中取出的EXTRA_LEVEL是当前的电量,从0-100,EXTRA_SCALE为最大电量,通常是100,若电池用了几年,消耗了不少,可能会低于100

public static int getBatteryPercentageImmediately(Context context) {
    Intent batIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    if (batIntent != null) {
        int level = batIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        int scale = batIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
        if (scale > 0) {
            return level * 100 / scale;
        }
    }
    return -1;
}

其他广播的监听

收到除ACTION_BATTERY_CHANGED之外的广播时,会将其记录到对应的变量中,用于标明当前状态的变化,并将此变化值记录到BatteryStatsFeature对象中,所以其实可以看到BatteryStatsFeature就是一个电量信息的载体,并且是实时记录,因此外界只要拿到BatteryStatsFeature就可以获取到实时变化的电量信息。

int devStat = -1;
boolean notifyStateChanged = false, notifyBatteryLowChanged = false;
switch (action) {
    case Intent.ACTION_SCREEN_ON:
        devStat = AppStats.DEV_STAT_SCREEN_ON;
        notifyStateChanged = true;
        break;
    case Intent.ACTION_SCREEN_OFF:
        devStat = AppStats.DEV_STAT_SCREEN_OFF;
        notifyStateChanged = true;
        break;
    case Intent.ACTION_POWER_CONNECTED:
        devStat = AppStats.DEV_STAT_CHARGING;
        notifyStateChanged = true;
        break;
    case Intent.ACTION_POWER_DISCONNECTED:
        devStat = AppStats.DEV_STAT_UN_CHARGING;
        notifyStateChanged = true;
        break;
    case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
        devStat = BatteryCanaryUtil.isDeviceOnIdleMode(context) ? AppStats.DEV_STAT_DOZE_MODE_ON : AppStats.DEV_STAT_DOZE_MODE_OFF;
        notifyStateChanged = true;
        break;
    case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
        devStat = BatteryCanaryUtil.isDeviceOnPowerSave(context) ? AppStats.DEV_STAT_SAVE_POWER_MODE_ON : AppStats.DEV_STAT_SAVE_POWER_MODE_OFF;
        notifyStateChanged = true;
        break;
    case Intent.ACTION_BATTERY_OKAY:
    case Intent.ACTION_BATTERY_LOW:
        notifyStateChanged = true;
        notifyBatteryLowChanged = true;
        break;
    default:
        break;
}

// 2. 记录状态变化
if (devStat != -1) {
    if (mCore != null) {
        BatteryStatsFeature feat = mCore.getMonitorFeature(BatteryStatsFeature.class);
        if (feat != null) {
            feat.statsDevStat(devStat);
        }
    }
}

// 3. 通知外界状态改变
if (notifyStateChanged) {
    onSateChangedEvent(intent);
}
//电量低,记录低电量状态
if (notifyBatteryLowChanged) {
    if (mCore != null) {
        BatteryStatsFeature feat = mCore.getMonitorFeature(BatteryStatsFeature.class);
        if (feat != null) {
            feat.statsBatteryEvent(action.equals(Intent.ACTION_BATTERY_LOW));
        }
    }
    onBatteryChangeEvent(intent);
}

至此start方法除feature逻辑外,都分析完了。

stop

stop方法也是调用的BatteryMonitorCore的stop,这里所做的就是将多个feature关闭,调用其onTurnOff方法。

public void stop() {
    synchronized (BatteryMonitorCore.class) {
        if (mTurnOn) {
            //移除所有消息
            mHandler.removeCallbacksAndMessages(null);
            //关闭feature
            for (MonitorFeature plugin : mConfig.features) {
                plugin.onTurnOff();
            }
            mTurnOn = false;
        }
    }
}

电量信息的获取

前边分析的内容其实是一个电量信息存储的过程,那么存起来之后怎么取出来呈现呢?就是通过下面这个方法,并且获取到的都是实时的信息。如果要获取其他信息比如wifi,则通过此方法传入WifiMonitorFeature即可。

BatteryCanary.getMonitorFeature(BatteryStatsFeature.class, new Consumer<BatteryStatsFeature>() {
    @Override
    public void accept(final BatteryStatsFeature batteryStatsFeature) {
        procTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                
            }
        });
    }
});

总结

今天我们简单分析了一下BatteryMonitorPlugin的源码作为电量优化分析的起点,可以大概的了解到,BatteryMonitorPlugin的工作机制大致可以分为两种,一种是通过各个feature类进行的不同场景下的信息统计;另一种是通过监听如电量、亮屏灭屏等广播来获取信息。今天的分析重点在于第二种,这种处理方式较简单,监听广播,更新信息。对于第一种不同类型的feature监听这一块,限于篇幅原因,不能过多的深入,BatteryMonitorPlugin包含的feature类型范围很广,无法在一篇文章中穷尽,所以需要分几篇专题文章来进行逐个分析,后边会陆陆续续更新。