Android中低电量的处理方法

2019-04-14 22:09发布

前段时间解了个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      262     15   下面代码是具体发送 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      265     20   按照如上分析,貌似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          * @hide 1659          */ 1660         public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; 1674         /** 1675          * URI for the low battery sound file. 1676          * @hide 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,