当前位置: 代码迷 >> Android >> Android中低电量的处置方法
  详细解决方案

Android中低电量的处置方法

热度:87   发布时间:2016-05-01 19:32:06.0
Android中低电量的处理方法

前段时间解了个Bug:Android系统在低电时充电,StatusBar上的充电图标不会变化,始终显示的是同一个图标

当时没有来得及整理,现在补一下

?

电池电量信息是从BatteryService中通过Intent发送出去的,在上一篇有讲到

frameworks/base/services/java/com/android/server/BatteryService.java

其中函数update负责读具体信息并发送

208 ????private?synchronized?final?void?update()?{

?

?

1、发送:Intent.ACTION_BATTERY_LOW?&& Intent.ACTION_BATTERY_OKAY)

首先需要判断是否需要发送低电量信息:Intent.ACTION_BATTERY_LOW

288 ????????????/*?The?ACTION_BATTERY_LOW?broadcast?is?sent?in?these?situations:
289 ?????????????*?-?is?just?un-plugged?(previously?was?plugged)?and?battery?level?is
290 ?????????????*???less?than?or?equal?to?WARNING,?or
291 ?????????????*?-?is?not?plugged?and?battery?level?falls?to?WARNING?boundary
292 ?????????????*???(becomes?<=?mLowBatteryWarningLevel).
293 ?????????????*/
294 ????????????final?boolean?sendBatteryLow?=?!plugged
295 ????????????????&&?mBatteryStatus?!=?BatteryManager.BATTERY_STATUS_UNKNOWN
296 ????????????????&&?mBatteryLevel?<=?mLowBatteryWarningLevel
297 ????????????????&&?(oldPlugged?||?mLastBatteryLevel?>?mLowBatteryWarningLevel);
可以看到发送低电量信息会有两个条件,
1)当前不在充电状态,上次update时处于充电状态,并且电池电量小于等于mLowBatteryWarningLevel(低电量警告值)
2)当前不在充电状态,电池电量小于等于mLowBatteryWarningLevel(低电量警告值),并且上次update时,电量大于mLowBatteryWarningLevel(低电量警告值)
?
但是mLowBatteryWarningLevel这个值具体是多少呢?
127 ????????mLowBatteryWarningLevel?=?mContext.getResources().getInteger(
128 ????????????????com.android.internal.R.integer.config_lowBatteryWarningLevel);
从以上可以看出,是通过config.xml读取到的
文件位于frameworks/base/core/res/res/values/config.xml
261 ????<!--?Display?low?battery?warning?when?battery?level?dips?to?this?value?-->
262 ????<integer?name="config_lowBatteryWarningLevel">15</integer>
?
下面代码是具体发送
315 ????????????if?(sendBatteryLow)?{
316 ????????????????mSentLowBatteryBroadcast?=?true;
317 ????????????????statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
318 ????????????????mContext.sendBroadcast(statusIntent);
319 ????????????}?else?if?(mSentLowBatteryBroadcast?&&?mLastBatteryLevel?>=?mLowBatteryCloseWarningLevel)?{
320 ????????????????mSentLowBatteryBroadcast?=?false;
321 ????????????????statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
322 ????????????????mContext.sendBroadcast(statusIntent);
323 ????????????}
其中if分支是发送Intent.ACTION_BATTERY_LOW,else分析负责发送Intent.ACTION_BATTERY_OKAY);
if 分支只要满足上面两个条件就回发送,而else什么时候发送呢?
根据代码可以这么理解:当发送Intent.ACTION_BATTERY_LOW时,会把mSentLowBatteryBroadcast?置为true,
同时mBatteryLevel?会小于15;由于电池更新较快,也就是此update函数较频繁,可以推断mLastBatteryLevel?将会略大约else分支用到mBatteryLevel?的值(可能为16),是否会满足else分支的mLastBatteryLevel?>=?mLowBatteryCloseWarningLevel条件呢?
此时就需要我们来查看mLowBatteryCloseWarningLevel的值具体是多少,定义方式与mLowBatteryWarningLevel类似,如下
129 ????????mLowBatteryCloseWarningLevel?=?mContext.getResources().getInteger(
130 ????????????????com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
也是通过config.xml读取到的
文件位于frameworks/base/core/res/res/values/config.xml
264 ????<!--?Close?low?battery?warning?when?battery?level?reaches?this?value?-->
265 ????<integer?name="config_lowBatteryCloseWarningLevel">20</integer>
?
按照如上分析,貌似else分析永远都不会执行,也就是Intent.ACTION_BATTERY_OKAY不会发出
但其实有一种情况会执行的,当电池电量小于mLowBatteryWarningLevel,并且已经成功发送了Intent.ACTION_BATTERY_LOW信息,此时用户插上USB或充电器充电,当电池电量达到mLowBatteryCloseWarningLevel,此时才会发送Intent.ACTION_BATTERY_OKAY信息
?
?

2、接收:Intent.ACTION_BATTERY_LOW?&& Intent.ACTION_BATTERY_OKAY)

1)StatusBarPolicy.java

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java

这个类在2.3之前的版本中放在frameworks/base/services/java/com/android/server/status/目录下,现在做了代码归整

首先需要注册

671 ????????IntentFilter?filter?=?new?IntentFilter();

672
673 ????????//?Register?for?Intent?broadcasts?for...
674 ????????filter.addAction(Intent.ACTION_BATTERY_CHANGED);
675 ????????filter.addAction(Intent.ACTION_BATTERY_LOW);
676 ????????filter.addAction(Intent.ACTION_BATTERY_OKAY);
700 ????????mContext.registerReceiver(mIntentReceiver,?filter,?null,?mHandler);
?
接收部分
529 ????????public?void?onReceive(Context?context,?Intent?intent)?{
530 ????????????String?action?=?intent.getAction();
531 ????????????if?(action.equals(Intent.ACTION_BATTERY_CHANGED))?{
532 ????????????????updateBattery(intent);
533 ????????????}
534 ????????????else?if?(action.equals(Intent.ACTION_ALARM_CHANGED))?{
535 ????????????????updateAlarm(intent);
536 ????????????}
537 ????????????else?if?(action.equals(Intent.ACTION_SYNC_STATE_CHANGED))?{
538 ????????????????updateSyncState(intent);
539 ????????????}
540 ????????????else?if?(action.equals(Intent.ACTION_BATTERY_LOW))?{
541 ????????????????onBatteryLow(intent);
542 ????????????}
543 ????????????else?if?(action.equals(Intent.ACTION_BATTERY_OKAY)
544 ????????????????????||?action.equals(Intent.ACTION_POWER_CONNECTED))?{
545 ????????????????onBatteryOkay(intent);
546 ????????????}
?
当收到Intent.ACTION_BATTERY_LOW时,做如下处理
759 ????private?void?onBatteryLow(Intent?intent)?{
760 ????????if?(SHOW_LOW_BATTERY_WARNING)?{
761 ????????????if?(false)?{
762 ????????????????Slog.d(TAG,?"mPhoneState="?+?mPhoneState
763 ??????????????????????+?"?mLowBatteryDialog="?+?mLowBatteryDialog
764 ??????????????????????+?"?mBatteryShowLowOnEndCall="?+?mBatteryShowLowOnEndCall);
765 ????????????}
766
767 ????????????if?(SHOW_BATTERY_WARNINGS_IN_CALL?||?mPhoneState?==?TelephonyManager.CALL_STATE_IDLE)?{
768 ????????????????showLowBatteryWarning();
769 ????????????}?else?{
770 ????????????????mBatteryShowLowOnEndCall?=?true;
771 ????????????}
772 ????????}
773 ????}
其中SHOW_LOW_BATTERY_WARNING和SHOW_BATTERY_WARNINGS_IN_CALL?定义为常量?
115 ????private?static?final?boolean?SHOW_LOW_BATTERY_WARNING?=?true;
116 ????private?static?final?boolean?SHOW_BATTERY_WARNINGS_IN_CALL?=?true;
1.1) 可以看到当此时电话状态为TelephonyManager.CALL_STATE_IDLE状态时,做如下处理,否则标记mBatteryShowLowOnEndCall为true
794 ????private?void?showLowBatteryWarning()?{
795 ????????closeLastBatteryView();
796
797 ????????//?Show?exact?battery?level.
798 ????????CharSequence?levelText?=?mContext.getString(
799 ????????????????????R.string.battery_low_percent_format,?mBatteryLevel);
800
801 ????????if?(mBatteryLevelTextView?!=?null)?{
802 ????????????mBatteryLevelTextView.setText(levelText);
803 ????????}?else?{
804 ????????????View?v?=?View.inflate(mContext,?R.layout.battery_low,?null);
805 ????????????mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
806
807 ????????????mBatteryLevelTextView.setText(levelText);
808
809 ????????????AlertDialog.Builder?b?=?new?AlertDialog.Builder(mContext);
810 ????????????????b.setCancelable(true);
811 ????????????????b.setTitle(R.string.battery_low_title);
812 ????????????????b.setView(v);
813 ????????????????b.setIcon(android.R.drawable.ic_dialog_alert);
814 ????????????????b.setPositiveButton(android.R.string.ok,?null);
815
816 ????????????????final?Intent?intent?=?new?Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
817 ????????????????intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
818 ????????????????????????|?Intent.FLAG_ACTIVITY_MULTIPLE_TASK
819 ????????????????????????|?Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
820 ????????????????????????|?Intent.FLAG_ACTIVITY_NO_HISTORY);
821 ????????????????if?(intent.resolveActivity(mContext.getPackageManager())?!=?null)?{
822 ????????????????????b.setNegativeButton(R.string.battery_low_why,
823 ????????????????????????????new?DialogInterface.OnClickListener()?{
824 ????????????????????????public?void?onClick(DialogInterface?dialog,?int?which)?{
825 ????????????????????????????mContext.startActivity(intent);
826 ????????????????????????????if?(mLowBatteryDialog?!=?null)?{
827 ????????????????????????????????mLowBatteryDialog.dismiss();
828 ????????????????????????????}
829 ????????????????????????}
830 ????????????????????});
831 ????????????????}
832
833 ????????????AlertDialog?d?=?b.create();
834 ????????????d.setOnDismissListener(mLowBatteryListener);
835 ????????????d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
836 ????????????d.show();
837 ????????????mLowBatteryDialog?=?d;
838 ????????}
839
840 ????????final?ContentResolver?cr?=?mContext.getContentResolver();
841 ????????if?(Settings.System.getInt(cr,
842 ????????????????Settings.System.POWER_SOUNDS_ENABLED,?1)?==?1)
843 ????????{
844 ????????????final?String?soundPath?=?Settings.System.getString(cr,
845 ????????????????Settings.System.LOW_BATTERY_SOUND);
846 ????????????if?(soundPath?!=?null)?{
847 ????????????????final?Uri?soundUri?=?Uri.parse("file://"?+?soundPath);
848 ????????????????if?(soundUri?!=?null)?{
849 ????????????????????final?Ringtone?sfx?=?RingtoneManager.getRingtone(mContext,?soundUri);
850 ????????????????????if?(sfx?!=?null)?{
851 ????????????????????????sfx.setStreamType(AudioManager.STREAM_SYSTEM);
852 ????????????????????????sfx.play();
853 ????????????????????}
854 ????????????????}
855 ????????????}
856 ????????}
857 ????}
此函数主要分两部分,第一部分是根据mBatteryLevelTextView是否为null,有选择的创建并显示mLowBatteryDialog,为什么不始终创建呢?
这时候需要看一下mLowBatteryListener
881 ????private?DialogInterface.OnDismissListener?mLowBatteryListener
882 ????????????=?new?DialogInterface.OnDismissListener()?{
883 ????????public?void?onDismiss(DialogInterface?dialog)?{
884 ????????????mLowBatteryDialog?=?null;
885 ????????????mBatteryLevelTextView?=?null;
886 ????????}
887 ????};
可以看出,如果mLowBatteryDialog已经被Dismiss掉,则选择创建mLowBatteryDialog,如果没有Dismiss,为了节省资源,只需要更新mLowBatteryDialog中的界面就可以了。
?
第二部分则是低电提示音,在网上看帖子,这个功能令很多人恼火啊,特别是学生深受其害,那么是否让用户有选择的打开此功能呢?
实现这部分功能需要两步
POWER_SOUNDS_ENABLED
841 ????????if?(Settings.System.getInt(cr,
842 ????????????????Settings.System.POWER_SOUNDS_ENABLED,?1)?==?1)
我们知道Settings.System.POWER_SOUNDS_ENABLED是一个系统属性,通过在源码中并没有查找到有用此属性的地方
Settings.System.LOW_BATTERY_SOUND
844 ????????????final?String?soundPath?=?Settings.System.getString(cr,
845 ????????????????Settings.System.LOW_BATTERY_SOUND);
846 ????????????if?(soundPath?!=?null)?{
?
定义在文件Settings.java
frameworks/base/core/java/android/provider/Settings.java
1656 ????????/**
1657 ?????????*?Whether?to?play?a?sound?for?low-battery?alerts.
1658 [email protected]
1659 ?????????*/
1660 ????????public?static?final?String?POWER_SOUNDS_ENABLED?=?"power_sounds_enabled";
1674 ????????/**
1675 ?????????*?URI?for?the?low?battery?sound?file.
1676 [email protected]
1677 ?????????*/
1678 ????????public?static?final?String?LOW_BATTERY_SOUND?=?"low_battery_sound";
通过如下方式读取默认值
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
1081 ????private?void?loadUISoundEffectsSettings(SQLiteStatement?stmt)?{
1082 ????????loadIntegerSetting(stmt,?Settings.System.POWER_SOUNDS_ENABLED,
1083 ????????????R.integer.def_power_sounds_enabled);
1084 ????????loadStringSetting(stmt,?Settings.System.LOW_BATTERY_SOUND,
1085 ????????????R.string.def_low_battery_sound);
?
1098 ????????loadIntegerSetting(stmt,?Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
1099 ????????????R.integer.def_lockscreen_sounds_enabled);
1100 ????????loadStringSetting(stmt,?Settings.System.LOCK_SOUND,
1101 ????????????R.string.def_lock_sound);
1102 ????????loadStringSetting(stmt,?Settings.System.UNLOCK_SOUND,
1103 ????????????R.string.def_unlock_sound);
上面包括是否开启解锁屏声音,默认值定义在如下文件中
61 ????<!--?user?interface?sound?effects?-->
62 ????<integer?name="def_power_sounds_enabled">1</integer>
63????<string?name="def_low_battery_sound"?translatable="false">/system/media/audio/ui/LowBattery.ogg</string>
64 ????<integer?name="def_dock_sounds_enabled">0</integer>
69 ????<integer?name="def_lockscreen_sounds_enabled">0</integer>
70 ????<string?name="def_lock_sound"?translatable="false">/system/media/audio/ui/Lock.ogg</string>
71 ????<string?name="def_unlock_sound"?translatable="false">/system/media/audio/ui/Unlock.ogg</string>
?
通过以上分析可以知道,低点提示音始终是有的,并且用户不可以定制
有了以上的分析,可以很清楚的知道,可以在Settings应用里加个可以配置低点提示音的功能
如可以在SoundSettings中多定义一个CheckboxPreference用来控制,当用户选中时,操作也很简单
final?ContentResolver?cr?=?mContext.getContentResolver();
Settings.System.putInt(cr, Settings.System.POWER_SOUNDS_ENABLED,?0);
?
1.2)如果此时电话不为TelephonyManager.CALL_STATE_IDLE状态,而是如下两种状态
TelephonyManager.CALL_STATE_OFFHOOK
TelephonyManager.CALL_STATE_RINGING
当电话状态变更为CALL_STATE_IDLE时,会调用如下函数
859 ????private?final?void?updateCallState(int?state)?{
860 ????????mPhoneState?=?state;
861 ????????if?(false)?{
862 ????????????Slog.d(TAG,?"mPhoneState="?+?mPhoneState
863 ????????????????????+?"?mLowBatteryDialog="?+?mLowBatteryDialog
864 ????????????????????+?"?mBatteryShowLowOnEndCall="?+?mBatteryShowLowOnEndCall);
865 ????????}
866 ????????if?(mPhoneState?==?TelephonyManager.CALL_STATE_IDLE)?{
867 ????????????if?(mBatteryShowLowOnEndCall)?{
868 ????????????????if?(!mBatteryPlugged)?{
869 ????????????????????showLowBatteryWarning();
870 ????????????????}
871 ????????????????mBatteryShowLowOnEndCall?=?false;
872 ????????????}
873 ????????}?else?{
874 ????????????if?(mLowBatteryDialog?!=?null)?{
875 ????????????????mLowBatteryDialog.dismiss();
876 ????????????????mBatteryShowLowOnEndCall?=?true;
877 ????????????}
878 ????????}
879 ????}
由于mBatteryShowLowOnEndCall已经被置为true,如果此时没有在充电状态,则也会调到showLowBatteryWarning,与以上的处理方式一样。
?
?
以上知识处理了Intent.ACTION_BATTERY_LOW,下面简单看一下如何处理Intent.ACTION_BATTERY_OKAY
775 ????private?void?onBatteryOkay(Intent?intent)?{
776 ????????if?(mLowBatteryDialog?!=?null
777 ????????????????&&?SHOW_LOW_BATTERY_WARNING)?{
778?????????????mLowBatteryDialog?.dismiss();
779 ????????????mBatteryShowLowOnEndCall?=?false;
780 ????????}
781 ????}
从以上代码中可以看出只是把mLowBatteryDialog给Dismiss掉,并把mBatteryShowLowOnEndCall?复位
?
?

1)Phone应用

还有Phone应用也会接收Intent.ACTION_BATTERY_LOW消息,但是跟下去,看懂了代码实现者的意图,如果在通话中,电池电量较低时,会给用户一个提示,但是最终并没有真正的实现这一功能。

首先PhoneApp中注册监听Intent.ACTION_BATTERY_LOW

packages/apps/Phone/src/com/android/phone/PhoneApp.java

499 ????????????intentFilter.addAction(Intent.ACTION_BATTERY_LOW);
?
1440 ????????????}?else?if?(action.equals(Intent.ACTION_BATTERY_LOW))?{
1441 ????????????????if?(VDBG)?Log.d(LOG_TAG,?"mReceiver:?ACTION_BATTERY_LOW");
1442 ????????????????notifier.sendBatteryLow();??//?Play?a?warning?tone?if?in-call
通过CallNotifier来处理信息
packages/apps/Phone/src/com/android/phone/CallNotifier.java
1222 ????/**
1223 ?????*?Posts?a?PHONE_BATTERY_LOW?event,?causing?us?to?play?a?warning
1224 ?????*?tone?if?the?user?is?in-call.
1225 ?????*/
1226 ????/*?package?*/?void?sendBatteryLow()?{
1227 ????????Message?message?=?Message.obtain(this,?PHONE_BATTERY_LOW);
1228 ????????sendMessage(message);
1229 ????}
通过Handler来处理PHONE_BATTERY_LOW信息
266 ????????????case?PHONE_BATTERY_LOW:
267 ????????????????onBatteryLow();
268 ????????????????break;

?

1231 ????private?void?onBatteryLow()?{
1232 ????????if?(DBG)?log("onBatteryLow()...");
1233
1234 ????????//?A?"low?battery"?warning?tone?is?now?played?by
1235 ????????//?StatusBarPolicy.updateBattery().
1236 ????}
从以上代码可以看出,Phone中并没有实现此功能,说是通过StatusBarPolicy.updateBattery()来实现
而StatusBarPolicy中的代码非常简单,根本就没有实现此功能

?

?

?PS:

?其实在BatteryService函数update中还会发送如下信息,如果有哪位同学想要研究的话,可以接着研究

1)Intent.ACTION_POWER_CONNECTED

2)Intent.ACTION_POWER_DISCONNECTED

301 ????????????//?Separate?broadcast?is?sent?for?power?connected?/?not?connected
302 ????????????//?since?the?standard?intent?will?not?wake?any?applications?and?some
303 ????????????//?applications?may?want?to?have?smart?behavior?based?on?this.
304 ????????????Intent?statusIntent?=?new?Intent();
305 ????????????statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
306 ????????????if?(mPlugType?!=?0?&&?mLastPlugType?==?0)?{
307 ????????????????statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
308 ????????????????mContext.sendBroadcast(statusIntent);
309 ????????????}
310 ????????????else?if?(mPlugType?==?0?&&?mLastPlugType?!=?0)?{
311 ????????????????statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
312 ????????????????mContext.sendBroadcast(statusIntent);
313 ????????????}
  相关解决方案