Pull to refresh

Оптимизация расхода батареи

Reading time 5 min
Views 23K
image

Введение


В этой статье приводится несколько советов по оптимизации расхода батареи приложениями.
Отключая фоновые сервисы обновлений при потере соединения или уменьшая частоту обновлений при низком уровне заряда батареи можно существенно минимизировать влияние работы приложения на батарею.
Некоторые фоновые сервисы (загрузка из сети обновлений или контента приложения, сложные расчеты и т.д.) целесообразно отключать или снижать частоту их запуска при низком уровне заряда батареи. Для подобных действий важно принимать во внимание несколько факторов, как, например, текущее состояние зарядки устройства, наличие подключенной док-станции или наличие соединения с сетью. Ниже представлены способы получения этих значений и мониторинга их изменений.

Текущее состояние зарядки


Класс BatteryManager рассылает информацию об уровне и состоянии заряда батареи в соответствующем интенте. При этом можно зарегистрировать BroadcastReceiver и своевременно получать информацию о состоянии батареи. А можно получить эти данные единоразово без регистрации receiver'a:

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

Из полученного интента берется информацию о текущем уровне заряда батареи, о состоянии зарядки, а также о том идет зарядка от сети переменного тока (AC) или usb:

public void getBatteryStatus(Intent batteryIntent) {
    // Заряжается ли батарея (или уже заряжена) ?
    int status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
    boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                     status == BatteryManager.BATTERY_STATUS_FULL;

    //Каким образом проходит зарядка?
    int chargePlug = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
    boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
    boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
}

Обычно стоит повысить до максимума частоту фоновых апдейтов приложения, если устройство заряжается от сети переменного тока, снизить если зарядка идет через usb и сделать минимальной если устройство вообще не находится в состоянии зарядки.
В том же случае, когда необходимо мониторить состояние зарядки устройства, следует зарегистрировать в манифесте приложения BroadcastReceiver, который будет получать сообщения о подключении/отключении зарядного устройства. В intent-filter при этом необходимо добавить ACTION_POWER_CONNECTED и ACTION_POWER_DISCONNECTED:

<receiver android:name=".PowerConnectionReceiver">
  <intent-filter>
    <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
  </intent-filter>
</receiver>


public class PowerConnectionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) { 
        getBatteryStatus(intent);   
    }
}

Текущий заряд батареи


Текущий относительный заряд батареи можно вычислить из значений текущего заряда и максимального заряда:

int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float)scale;

Стоит упомянуть, что регулярный мониторинг состояния батареи является ресурсозатратным для самой батареи. Поэтому рекомендуется мониторить только определенные изменения в уровне заряда, а именно уровень низкого заряда:

<receiver android:name=".BatteryLevelReceiver">
<intent-filter>
  <action android:name="android.intent.action.ACTION_BATTERY_LOW"/>
  <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/>
  </intent-filter>
</receiver>

Хорошей практикой является отключение всех фоновых обновлений или вычислений, когда уровень заряда батареи становится низким.

Состояние и тип док-станции


Существует большое количество док-станций для Android устройств. Например, док-станция в автомобиле или док-клавиатура у Asus Transformer. При этом большинство док-станций заряжают само устройство.
Получить информацию о типе и состоянии док-станции можно из action'a ACTION_DOCK_EVENT. Ее можно получить единоразово:

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

либо подписаться на оповещения, добавив в манифесте BroadcastReceiver и указав action:

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

Информация о док-станции получается следующим образом:

public void getDockStatus(Intent dockIntent) {
    //определение подключения к доку
    int dockState = dockIntent.getIntExtra(Intent.EXTRA_DOCK_STATE, -1);
    boolean isDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;

    //определение типа дока
    boolean isCar = dockState == Intent.EXTRA_DOCK_STATE_CAR;
    boolean isDesk = dockState == Intent.EXTRA_DOCK_STATE_DESK || 
                 dockState == Intent.EXTRA_DOCK_STATE_LE_DESK ||
                 dockState == Intent.EXTRA_DOCK_STATE_HE_DESK;
}

Значения EXTRA_DOCK_STATE_HE_DESK и EXTRA_DOCK_STATE_LE_DESK появились только с API версии 11.

Статус соединения с сетью


Перед выполнением фоновых обновлений приложения или длительных загрузок из сети стоит проверять наличие соединения и его примерную скорость. Для этого можно воспользоваться классом ConnectivityManager.

Определение подключения к интернету:

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

Также иногда стоит определять тип текущего соединения перед началом загрузок. Это бывает важно, т.к. скорость соединения у мобильного интернета обычно ниже, чем у Wi-Fi, а цена траффика выше.

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

Подписаться на оповещения изменения состояния соединения можно добавив в манифест BroadcastReceiver указав ему соотвествующий action:

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

Так как подобные изменения могут происходить довольно часто, хорошей практикой является мониторить эти оповещения только в случае когда обновления или загрузки были отключены ранее. Обычно достаточно проверить наличие соединения перед началом обновлений и в случае, когда соединение отсутствует подписаться на оповещения.

Включение/выключение оповещений


Оповещения о состоянии батареи, док-станции и наличии соединения не рекомендуется постоянно держать включенными, т.к. они будут пробуждать устройство слишком часто. Лучшим решением будет включать оповещения только по необходимости.
Класс PackageManager позволяет изменять состояние элементов объявленных в манифесте, в том числе включать/выключать BroadcastReceiver'ы:

ComponentName receiver = new ComponentName(context, CustomReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);

С помощью подобной техники можно, например, при обрыве соединения оставлять включенным только оповещение об изменениях состояния сети. При появлении соединения можно отключать оповещения об изменениях сети и только проверять наличие соединения в текущий момент.
Подобной техникой также можно пользоваться чтобы отложить загрузку данных из сети до тех пор пока не будет соединения с большей пропускной способностью, например Wi-Fi.

Статья является свободным переводом набора советов из программы Android Training по оптимизации расхода заряда батареи приложениями.
Tags:
Hubs:
+46
Comments 12
Comments Comments 12

Articles