您的位置:首页 > 移动开发 > Android开发

Android5.1 电池充电剩余时间计算

2017-05-06 11:34 423 查看
android5.1手机在充电的时候,并且在锁屏界面的时候会显示还剩多少时间电池充满电。我们就这个机制进行下深入分析:

首先对电池的变化都会监听BatteryService发出的Intent.ACTION_BATTERY_CHANGED广播,因此在framework目录下全局搜索,结果发现在./base/packages/Keyguard/src/com/Android/keyguard/KeyguardUpdateMonitor.Java这个目录下,也就是keyguard中有对这个广播的监控

在KeyguardUpdateMonitor.java这个文件中

[java] view
plain copy

private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

public void onReceive(Context context, Intent intent) {

final String action = intent.getAction();

if (DEBUG) Log.d(TAG, "received broadcast " + action);

if (Intent.ACTION_TIME_TICK.equals(action)

|| Intent.ACTION_TIME_CHANGED.equals(action)

|| Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {

mHandler.sendEmptyMessage(MSG_TIME_UPDATE);

} else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {//监听电池变化的广播

final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);

final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);

final int level = intent.getIntExtra(EXTRA_LEVEL, 0);

final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);

final Message msg = mHandler.obtainMessage(

MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));

mHandler.sendMessage(msg);//发消息

}

接下来我们搜索下MSG_BATTERY_UPDATE这个消息,

[java] view
plain copy

private void handleBatteryUpdate(BatteryStatus status) {

if (DEBUG) Log.d(TAG, "handleBatteryUpdate");

final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);

mBatteryStatus = status;

if (batteryUpdateInteresting) {

for (int i = 0; i < mCallbacks.size(); i++) {

KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();

if (cb != null) {

cb.onRefreshBatteryInfo(status);//接下来我们就搜一下谁定义了onRefreshBatteryInfo这个接口

}

}

}

}

结果在KeyguardIndicationController.java这个文件中

[java] view
plain copy

KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {

@Override

public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {

boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING

|| status.status == BatteryManager.BATTERY_STATUS_FULL;

mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;

mPowerCharged = status.isCharged();

updateIndication();

}

};

接下来看下updateIndication

[java] view
plain copy

private void updateIndication() {

if (mVisible) {

mTextView.switchIndication(computeIndication());

}

}

接下来就是看下如何计算电池剩余时间的

[java] view
plain copy

private String computeIndication() {

if (!TextUtils.isEmpty(mTransientIndication)) {

return mTransientIndication;

}

if (mPowerPluggedIn) {//当在充电时

return computePowerIndication();

}

return mRestingIndication;

}

private String computePowerIndication() {

if (mPowerCharged) {

return mContext.getResources().getString(R.string.keyguard_charged);

}

// Try fetching charging time from battery stats.

try {//剩余时间通过BatteryStatsService获取

long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();

if (chargingTimeRemaining > 0) {

String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(

mContext, chargingTimeRemaining);

return mContext.getResources().getString(

R.string.keyguard_indication_charging_time, chargingTimeFormatted);

}

} catch (RemoteException e) {

Log.e(TAG, "Error calling IBatteryStats: ", e);

}

// Fall back to simple charging label.

return mContext.getResources().getString(R.string.keyguard_plugged_in);

}

下面是mBatteryInfo的赋值

[java] view
plain copy

mBatteryInfo = IBatteryStats.Stub.asInterface(

ServiceManager.getService(BatteryStats.SERVICE_NAME));

然后看BatteryStatsService中的computeChargeTimeRemaining代码

[java] view
plain copy

public long computeChargeTimeRemaining() {

synchronized (mStats) {

long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());

return time >= 0 ? (time/1000) : time;

}

}

其中mStats是在BatteryStatsService构造的时候新建的:

[java] view
plain copy

BatteryStatsService(File systemDir, Handler handler) {

mStats = new BatteryStatsImpl(systemDir, handler);

}

最后我们就来分析下BatteryStatsImpl中的computeChargeTimeRemaining函数:

[java] view
plain copy

@Override

public long computeChargeTimeRemaining(long curTime) {

if (mOnBattery) {//当处于用电池的状态直接退出

// Not yet working.

Slog.e(TAG, "mOnBattery");

return -1;

}

/* Broken

int curLevel = mCurrentBatteryLevel;

int plugLevel = mDischargePlugLevel;

if (plugLevel < 0 || curLevel < (plugLevel+1)) {

return -1;

}

long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);

if (duration < 1000*1000) {

return -1;

}

long usPerLevel = duration/(curLevel-plugLevel);

return usPerLevel * (100-curLevel);

*/

if (mNumChargeStepDurations < 1) {

Slog.e(TAG, "mNumChargeStepDurations");

return -1;

}//mChargeStepDurations是一个数组,保存着每一个level对应的时间,而mNumChargeStepDurations是现在总的一个level

long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);

if (msPerLevel <= 0) {

Slog.e(TAG, "msPerLevel");

return -1;

}

Slog.e(TAG, "return time remianing" + (msPerLevel * (100-mCurrentBatteryLevel)) * 1000);

return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;//因此msperlevel代表每一个level需要多少时间,100-mCurrentBatteryLevel代表需要充电的电量

}

再来看看

[java] view
plain copy

private long computeTimePerLevel(long[] steps, int numSteps) {

// For now we'll do a simple average across all steps.

if (numSteps <= 0) {

return -1;

}

long total = 0;

for (int i=0; i<numSteps; i++) {

total += steps[i] & STEP_LEVEL_TIME_MASK;//高位保存的别的信息,需要将它去除

}

return total / numSteps;//所有的level的时间和除以总的level,就是每个level的平均时间

但是现在的关键mChargeStepDurations,和mNumChargeStepDurations这两个成员变量如何获得。

当在BatteryService更新电池信息的时候,如下:会调用BatteryStatsService的setBatteryState函数

[java] view
plain copy

try {

mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,

mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,

mBatteryProps.batteryVoltage);

} catch (RemoteException e) {

// Should never happen.

}

而在BatteryStatsService的setBatteryState函数中:检查下权限然后调用了BatteryStatsImpl的setBatteryState函数

[java] view
plain copy

public void setBatteryState(int status, int health, int plugType, int level,

int temp, int volt) {

enforceCallingPermission();

mStats.setBatteryState(status, health, plugType, level, temp, volt);

}

接下来我们来看下核心函数:

[java] view
plain copy

public void setBatteryState(int status, int health, int plugType, int level,

int temp, int volt) {

synchronized(this) {

final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;

final long uptime = SystemClock.uptimeMillis();

final long elapsedRealtime = SystemClock.elapsedRealtime();

int oldStatus = mHistoryCur.batteryStatus;

if (!mHaveBatteryLevel) {

mHaveBatteryLevel = true;

// We start out assuming that the device is plugged in (not

// on battery). If our first report is now that we are indeed

// plugged in, then twiddle our state to correctly reflect that

// since we won't be going through the full setOnBattery().

if (onBattery == mOnBattery) {

if (onBattery) {

mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;

} else {

mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;

}

}

oldStatus = status;

}

if (onBattery) {

mDischargeCurrentLevel = level;

if (!mRecordingHistory) {

mRecordingHistory = true;

startRecordingHistory(elapsedRealtime, uptime, true);

}

} else if (level < 96) {

if (!mRecordingHistory) {

mRecordingHistory = true;

startRecordingHistory(elapsedRealtime, uptime, true);

}

}

mCurrentBatteryLevel = level;

if (mDischargePlugLevel < 0) {

mDischargePlugLevel = level;

}

if (onBattery != mOnBattery) {

mHistoryCur.batteryLevel = (byte)level;

mHistoryCur.batteryStatus = (byte)status;

mHistoryCur.batteryHealth = (byte)health;

mHistoryCur.batteryPlugType = (byte)plugType;

mHistoryCur.batteryTemperature = (short)temp;

mHistoryCur.batteryVoltage = (char)volt;

setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);

} else {

boolean changed = false;

if (mHistoryCur.batteryLevel != level) {

mHistoryCur.batteryLevel = (byte)level;

changed = true;

}

if (mHistoryCur.batteryStatus != status) {

mHistoryCur.batteryStatus = (byte)status;

changed = true;

}

if (mHistoryCur.batteryHealth != health) {

mHistoryCur.batteryHealth = (byte)health;

changed = true;

}

if (mHistoryCur.batteryPlugType != plugType) {

mHistoryCur.batteryPlugType = (byte)plugType;

changed = true;

}

if (temp >= (mHistoryCur.batteryTemperature+10)

|| temp <= (mHistoryCur.batteryTemperature-10)) {

mHistoryCur.batteryTemperature = (short)temp;

changed = true;

}

if (volt > (mHistoryCur.batteryVoltage+20)

|| volt < (mHistoryCur.batteryVoltage-20)) {

mHistoryCur.batteryVoltage = (char)volt;

changed = true;

}

if (changed) {

addHistoryRecordLocked(elapsedRealtime, uptime);

}

long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)

| (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)

| (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);//主要在高位存储一些模式与我们的关系不大

if (onBattery) {

if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {

mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,

mNumDischargeStepDurations, mLastDischargeStepTime,

mLastDischargeStepLevel - level, modeBits, elapsedRealtime);

mLastDischargeStepLevel = level;

mMinDischargeStepLevel = level;

mLastDischargeStepTime = elapsedRealtime;

mInitStepMode = mCurStepMode;

mModStepMode = 0;

}

} else {

if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {

mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,

mNumChargeStepDurations, mLastChargeStepTime,

level - mLastChargeStepLevel, modeBits, elapsedRealtime);

mLastChargeStepLevel = level;

mMaxChargeStepLevel = level;

mLastChargeStepTime = elapsedRealtime;//将mLastChargeStepTime 赋成当前时间值

mInitStepMode = mCurStepMode;

mModStepMode = 0;

}

}

}

if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {

// We don't record history while we are plugged in and fully charged.

// The next time we are unplugged, history will be cleared.

mRecordingHistory = DEBUG;

}

}

}

再对应下面的addLevelSteps函数,入参numStepLevels是level-mLastChargeStepLevel也就是代表这侧充了多少电,

[java] view
plain copy

private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,

int numStepLevels, long modeBits, long elapsedRealtime) {

if (lastStepTime >= 0 && numStepLevels > 0) {

long duration = elapsedRealtime - lastStepTime;//这次充电用了多少时间

for (int i=0; i<numStepLevels; i++) {将计算每个电池level的消耗的时间

System.arraycopy(steps, 0, steps, 1, steps.length-1);//这个函数的意思是将数组steps从位置0,长度steps.length-1,复制到位置1

long thisDuration = duration / (numStepLevels-i);

duration -= thisDuration;

if (thisDuration > STEP_LEVEL_TIME_MASK) {

thisDuration = STEP_LEVEL_TIME_MASK;

}

steps[0] = thisDuration | modeBits;//这个是将一些模式信息存在高位,与我们不是很相关

}

stepCount += numStepLevels;

if (stepCount > steps.length) {

stepCount = steps.length;

}

}

return stepCount;

}

有可能比较难理解,我们直接举个例子

比如我们充了5格电,用了30s,当i=0,thisDuration = 30 / 5 = 6,step[0] = 6;当 i =1,thisDuration = 24 / (5 -1) = 6;然后将整个数组往后延,这样每次保存的值都会往后延。

最前面是最新的电池充电时间,mNumChargeStepDurations是所有的level,哪怕再次调用这个函数,数组还是往后延,该保存的值都保存了。

因此计算每格电池的平均充电时间,每格电池所花的时间之和除以就是所有的level即mNumChargeStepDurations。就是平均每格电池的充电时间。

至此BatteryStatsImpl中计算充电电池还剩多少时间我们已经分析完了。

接下来我们再来看下"batterystats.bin"这个文件

先来看它的读取:会在readSummaryFromParcel函数中,将成员变量都读出来。

[java] view
plain copy

public void readLocked() {

if (mFile == null) {

Slog.w("BatteryStats", "readLocked: no file associated with this instance");

return;

}

mUidStats.clear();

try {

File file = mFile.chooseForRead();

if (!file.exists()) {

return;

}

FileInputStream stream = new FileInputStream(file);

byte[] raw = BatteryStatsHelper.readFully(stream);

Parcel in = Parcel.obtain();

in.unmarshall(raw, 0, raw.length);

in.setDataPosition(0);

stream.close();

readSummaryFromParcel(in);

} catch(Exception e) {

Slog.e("BatteryStats", "Error reading battery statistics", e);

}

readSummaryFromParcel函数,从"batterystats.bin"这个文件读取各个成员变量。

[java] view
plain copy

public void readSummaryFromParcel(Parcel in) {

final int version = in.readInt();

if (version != VERSION) {

Slog.w("BatteryStats", "readFromParcel: version got " + version

+ ", expected " + VERSION + "; erasing old stats");

return;

}

readHistory(in, true);

mStartCount = in.readInt();

mUptime = in.readLong();

mRealtime = in.readLong();

mStartClockTime = in.readLong();

mStartPlatformVersion = in.readString();

mEndPlatformVersion = in.readString();

mOnBatteryTimeBase.readSummaryFromParcel(in);

mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);

mDischargeUnplugLevel = in.readInt();

mDischargePlugLevel = in.readInt();

mDischargeCurrentLevel = in.readInt();

mCurrentBatteryLevel = in.readInt();

mLowDischargeAmountSinceCharge = in.readInt();

mHighDischargeAmountSinceCharge = in.readInt();

mDischargeAmountScreenOnSinceCharge = in.readInt();

mDischargeAmountScreenOffSinceCharge = in.readInt();

mNumDischargeStepDurations = in.readInt();

in.readLongArray(mDischargeStepDurations);

mNumChargeStepDurations = in.readInt();

然后还有两个写的函数,一个同步一个异步

[java] view
plain copy

public void writeAsyncLocked() {

writeLocked(false);

}

public void writeSyncLocked() {

writeLocked(true);

}

void writeLocked(boolean sync) {

if (mFile == null) {

Slog.w("BatteryStats", "writeLocked: no file associated with this instance");

return;

}

if (mShuttingDown) {

return;

}

Parcel out = Parcel.obtain();

writeSummaryToParcel(out, true);

mLastWriteTime = SystemClock.elapsedRealtime();

if (mPendingWrite != null) {

mPendingWrite.recycle();

}

mPendingWrite = out;

if (sync) {

commitPendingDataToDisk();

} else {

BackgroundThread.getHandler().post(new Runnable() {

@Override public void run() {

commitPendingDataToDisk();

}

});

}

}

在ActivityManagerService中新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取"batterystats.bin"的值,来初始化自己的成员变量。

[java] view
plain copy

mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);

mBatteryStatsService.getActiveStatistics().readLocked();

mBatteryStatsService.getActiveStatistics().writeAsyncLocked();

比如在关机的时候进行写,但这时候是同步的。

我们怎么理解新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取"batterystats.bin"的值,来初始化自己的成员变量。

比如说充电,一开机,之前的充电的每格电池的充电时间都没有,通过从"batterystats.bin"文件中读取,就可以将上次开机的状态知道了。

其实仔细看代码,知道插入usb充电时,由于mOnBattery 改变了会调用setOnBatteryLocked函数,而这时候将mLastChargeStepTime = -1;mNumChargeStepDurations = 0;

[java] view
plain copy

void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,

final int oldStatus, final int level) {

boolean doWrite = false;

Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);

m.arg1 = onBattery ? 1 : 0;

mHandler.sendMessage(m);

final long uptime = mSecUptime * 1000;

final long realtime = mSecRealtime * 1000;

final boolean screenOn = mScreenState == Display.STATE_ON;

if (onBattery) {

...........

} else {

Slog.e("kangchen", "setOnBatteryLocked mNumChargeStepDurations = 0;");

mOnBattery = mOnBatteryInternal = onBattery;

pullPendingStateUpdatesLocked();

mHistoryCur.batteryLevel = (byte)level;

mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;

if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "

+ Integer.toHexString(mHistoryCur.states));

addHistoryRecordLocked(mSecRealtime, mSecUptime);

mDischargeCurrentLevel = mDischargePlugLevel = level;

if (level < mDischargeUnplugLevel) {

mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;

mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;

}

updateDischargeScreenLevelsLocked(screenOn, screenOn);

updateTimeBasesLocked(false, !screenOn, uptime, realtime);

mNumChargeStepDurations = 0;

mLastChargeStepLevel = level;

mMaxChargeStepLevel = level;

mLastChargeStepTime = -1;

mInitStepMode = mCurStepMode;

mModStepMode = 0;

}

.............

下次BatteryService再次调用BatterStatsImpl的setBatteryState的时候,首先先要电量充了一格电才能进这个分支,但是由于这时候mLastChargeStepTime 还是-1,所以进addLevelSteps函数,直接返回,mNumChargeStepDurations 还是为0 。所以需要再充一格电才能进去,并且mNumChargeStepDurations 不为0

[java] view
plain copy

} else {

if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {

mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,

mNumChargeStepDurations, mLastChargeStepTime,

level - mLastChargeStepLevel, modeBits, elapsedRealtime);

mLastChargeStepLevel = level;

mMaxChargeStepLevel = level;

mLastChargeStepTime = elapsedRealtime;

mInitStepMode = mCurStepMode;

mModStepMode = 0;

}

}

[java] view
plain copy

private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,

int numStepLevels, long modeBits, long elapsedRealtime) {

if (lastStepTime >= 0 && numStepLevels > 0) {//lastStepTime为0 if进不去

long duration = elapsedRealtime - lastStepTime;

for (int i=0; i<numStepLevels; i++) {

System.arraycopy(steps, 0, steps, 1, steps.length-1);

long thisDuration = duration / (numStepLevels-i);

duration -= thisDuration;

if (thisDuration > STEP_LEVEL_TIME_MASK) {

thisDuration = STEP_LEVEL_TIME_MASK;

}

steps[0] = thisDuration | modeBits;

}

stepCount += numStepLevels;

if (stepCount > steps.length) {

stepCount = steps.length;

}

}

return stepCount;

}

而且只有当mNumChargeStepDurations 大于0的时候,computeChargeTimeRemaining计算才会有正常返回值。

[java] view
plain copy

public long computeChargeTimeRemaining(long curTime) {

if (mOnBattery) {

// Not yet working.

return -1;

}

/* Broken

int curLevel = mCurrentBatteryLevel;

int plugLevel = mDischargePlugLevel;

if (plugLevel < 0 || curLevel < (plugLevel+1)) {

return -1;

}

long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);

if (duration < 1000*1000) {

return -1;

}

long usPerLevel = duration/(curLevel-plugLevel);

return usPerLevel * (100-curLevel);

*/

if (mNumChargeStepDurations < 1) {

return -1;

}

long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);

if (msPerLevel <= 0) {

return -1;

}

return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;

}

因此插上usb线,在锁屏状态下。需要充两格电或者以上,才会有剩余时间的显示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: