Android 的电池消耗优化 II-监测电量等级和充电状态

概述:

当你正在改变你的后台刷新频率以降低刷新对电池寿命的影响时, 检查当前的电量等级和充电状态是一个很好的开端. 执行APP更新对电池寿命的影响依赖于电量等级和设备的充电状态. 当设备通过AC(交流电)充电的时候, 执行更新的影响是微不足道的, 所以在任何时候只要设备连接到了墙壁式充电器的时候, 你都可以最大化你的刷新频率. 相反的, 如果设备没在充电, 则应该减少更新的频率来帮助延迟电池寿命. 类似的, 应该检查电池的电量等级来适当的减少刷新频率(甚至是停止).

判断当前的充电状态:

从判断当前的充电状态来开始. BatteryManager会通过一个sticky intent广播所有的电池和充电细节. 因为它是一个sticky intent, 所以你不需要注册一个BroadcastReceive, 只需要简单的调用registerReceiver, 并传给它一个null就可以了, 当前的电池状态intent就返回来了. 你也可以传一个真实的BroadcastReceiver对象, 但是这没有必要.

IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);

你可以得到当前的充电状态和设备是通过USB或者AC充电的(如果它正在充电的话).

// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                     status == BatteryManager.BATTERY_STATUS_FULL;

// How are we charging?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;

通常在设备连接到一个AC充电器的时候, 你应该最大频率的进行后台更新, 如果充电是通过USB的, 那么就减少些频率, 如果没在充电, 那么就更少.

监测充电状态的变化:

充电状态会在设备插入插销或拔出的时候改变, 所以监测充电状态的变化并改变刷新频率很重要. 任何时候只要设备连接或断开电源BatteryManager就会广播一个action. 收到这些事件很重要, 就算是你的APP没有在运行– 这些事件应该会影响你的APP在后台更新的频率– 所以你应该注册一个BroadcastReceiver在manifest中使用intent filter来监听ACTION_POWER_CONNECTED和ACTION_POWER_DISCONNECTED事件.

 android:name=".PowerConnectionReceiver">
  
     android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
     android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
  

实现对应的BroadcastReceiver, 你就可以获取到当前的充电状态了:

public class PowerConnectionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                            status == BatteryManager.BATTERY_STATUS_FULL;

        int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
        boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
        boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
    }
}

确定当前的电量等级:

在某些情况下, 确定当前的电量等级也很重要. 在电池电量低于某一等级的时候你可能会选择减少后台刷新的频率. 你可以通过获取当前电量等级和缩放电池状态找到当前的电量:

int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);

float batteryPct = level / (float)scale;

监听电量等级的显著变化:

你不能简单的连续不断的监测电池状态, 而且你也不需要这么干. 一般来说, 持续不断的监测电池电量可能比APP本身的行为更加耗电, 所以只监测电池的明显变化是一个好习惯– 特别是当设备进入或离开低电量状态的时候.

下面的manifest代码段在设备电量变低或者脱离低电量的时候会分别收到ACTION_BATTERY_LOW和ACTION_BATTERY_OKAY:

 android:name=".BatteryLevelReceiver">

   android:name="android.intent.action.ACTION_BATTERY_LOW"/>
   android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
  

应该在电量很低的情况下关闭所有的APP后台刷新. 在设备即将关闭的时候, 多么即时的数据也没有意义了.

判断和监测接口(底座)的状态和种类:

Android设备可以被接入多种不同种类的接口中. 包括汽车, 家用接口和数字与模拟接口.接口的种类通常跟充电状态紧密相连, 因为各种接口都可以为设备提供电量. 接口状态如何影响你的更新频率取决于你的APP逻辑. 在一款运动类APP中你可能会在设备出在桌面接口时选择增加更新频率, 或者在汽车接口中选择完全禁用更新. 相反, 如果你的APP是一款实时路况类的APP, 那么可能会在接入汽车接口的时候选择最大频率更新. 接口状态也会通过一个sticky intent来广播, 让你可以通过它来查询设备是否接入了接口, 以及接口的种类.

判断当前接口的状态:

接口状态的详情可以通过广播来接收, action是ACTION_DOCK_EVENT. 因为它是sticky的, 所以你不需要注册一个广播. 可以通过registerReceiver并传一个null作为参数就可以了;

IntentFilter ifilter = new IntentFilter(Intent.ACTION_DOCK_EVENT);
Intent dockStatus = context.registerReceiver(null, ifilter);

可以从EXTRA_DOCK_STATE中获取当前接口状态:

int dockState = battery.getIntExtra(EXTRA_DOCK_STATE, -1);
boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;

判断当前接口类型:

如果设备接入了, 那么它可以被接入到任何下面四种接口之一:

汽车

桌面

低端(模拟)桌面

高端(数字)桌面

留意后面的两个选项只在Android API level 11中引入, 所以三种桌面类型放在一起检查要比单独检查数字或者模拟要好:

boolean isCar = dockState == EXTRA_DOCK_STATE_CAR;
boolean isDesk = dockState == EXTRA_DOCK_STATE_DESK ||
                 dockState == EXTRA_DOCK_STATE_LE_DESK ||
                 dockState == EXTRA_DOCK_STATE_HE_DESK;

监测接口状态或者类型的改变:

每当你的设备接入了或者没接入, ACTION_DOCK_EVENT都会广播. 要监测设备接入状态的改变, 只需要简单的在manifest中注册一个这样的广播:

 android:name="android.intent.action.ACTION_DOCK_EVENT"/>

你可以使用上面描述的方法来获得接口类型.

判断和监测网络连接类型:

有些最见的后台服务和重复警告(repeating alarms)都是计划定时更新APP数据(从互联网, 缓存数据, 或者执行长时间定时下载). 但是如果你并没有连接到互联网, 或者网络连接太慢不足以完成下载, 那么为什么还要唤醒设备来执行更新呢? 你可以使用ConnectivityManager来检查你是否真的连接到了互联网, 以及连接类型.

判断是否连接到了互联网:

如果没有连接到互联网, 那么就没有必要计划一个基于网络的更新. 下面的代码演示了如何使用ConnectivityManager来查询活动的网络并确定它是否是一个互联网连接:

ConnectivityManager cm =
        (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
 
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
                     activeNetwork.isConnectedOrConnecting();

判断网络连接的类型:

还可以判断当前可用连接的类型. 设备连接可以通过移动数据, WiMAX, WiFi, 和以太网来提供. 通过查询活动连接的类型, 你可以基于可用带宽来决定刷新的频率:

boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

移动数据要消耗的开销比WiFI高得多, 所以在大多数情况下, 当使用移动信号连接的时候应该降低你的APP更新频率. 类似的, 下载和其它的大流量工作应该在WiFi连接下执行. 如果已经停止了更新, 那么依然有必要监听连接事件以便在重新建立连接的时候恢复.

监测连接的变化:

在每次连接详情改变的时候, ConnectivityManager会广播CONNECTIVITY_ACTION(android.net.conn.CONNECTIVITY_CHANGE)这个action. 你可以注册一个BroadcastReceiver来监听这些改变并以此来恢复(或者暂停)你的后台更新:

 android:name="android.net.conn.CONNECTIVITY_CHANGE"/>

设备连接类型的改变可能会非常的频繁– 每次在移动数据和WiFi之间切换都会导致发送该广播. 所以在恢复更新和下载的时候, 应该监听该消息. 通常在更新之前执行一次这样的检查就足够了, 如果没有连接, 那么就暂停更新, 直到连接恢复.

按需操作Broadcast Receiver:

最简单的监测设备状态改变的方法是为你所监测的每一个状态创建一个BroadcastReceiver, 并将它们注册在APP的manifest中. 这种做法的一个副作用是你的APP将会在每次这些receiver被触发的时候唤醒设备– 可能比需要的要频繁的多. 一个更好的方法是运行时启用或者禁用Broadcast receiver. 这样就可以只在需要的时候出发这个receiver了.

 

你可以使用PackageManager来切换任意在manifest中定义的组件的启动状态. 包括任何Broadcast receiver:

ComponentName receiver = new ComponentName(context, myReceiver.class);

PackageManager pm = context.getPackageManager();

pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP)

通过这种方法, 如果你确定了连接已经丢失, 你可以禁用所有的连接改变receiver以外的其它receiver. 相反, 一旦你连接到了, 就可以停止监听连接改变的消息并简单的在更新之前检查是否在线即可.

你可以使用同样的方法来延迟那些需要高带宽的下载. 初始化一个receiver, 在监测到连接WiFi之后才开启下载.

 

参考: https://developer.android.com/training/monitoring-device-state/index.html

 

你可能感兴趣的:(Android,性能)