Думаю, что у каждого пользователя Android рано или поздно возникает необходимость понять, какое приложение за ночь съело всю батарею притом, что телефон лежал с погашенным экраном. Участь найденного виновника не завидна: чаще всего его просто удаляют. Итак, что же сделать, чтобы наши приложения использовали аккумулятор минимально? В статье я постараюсь дать ответ на этот вопрос и рассказать о подходах к уменьшению потребления аккумулятора, которые мне доводилось использовать.



Количественная оценка


Прежде всего, давайте договоримся, как будем измерять потребление батареи, чтобы наблюдать за прогрессом после тех или иных исправлений в коде. Все, что нам доступно, — это процент заряда батареи. На него и будем ориентироваться. Эта величина меняется в зависимости от очень многих факторов.

Чтобы уменьшить ошибку измерения, желательно:
  • использовать одно и то же устройство;
  • использовать одну и ту же часть шкалы (дело в том, что изменение на один процент заряда на разных частях шкалы означает изменение на разный заряд, например, от 100% до 99% обычно разряжается быстрее, чем от 90% до 89%);
  • перед началом серии тестов на батарею на устройстве желательно сделать factory reset;
  • перед каждым тестом перезагружать устройство;
  • во время одного теста разряжать устройство более чем на 10%, чтобы погрешность измерения была не очень велика;
  • не ставить и не запускать никаких новых приложений во время серии тестов, они в фоне могут потреблять батарею;
  • не менять настройки устройства: яркость экрана, настройки сети и т.д.;
  • не перемещать устройство на существенные расстояния (более нескольких метров) во время теста или при каждом тесте перемещать его по одной и той же траектории.

На что же уходит батарея?


Главные потребители батареи это:
  • работа CPU;
  • подсветка экрана;
  • работа GPU;
  • работа с сетью;
  • частые получения GPS-координат.

Думаю, этот список знаком многим Android-разработчикам. Самое интересное, каков вклад каждого из этих пунктов. Конечно, он может быть разный. И вот мои наблюдения.
  • CPU часто главный потребитель;
  • на подсветку экрана часто уходит не меньше, чем CPU;
    Очень важно не менять яркость подсветки во время сравнительных тестов. Иначе это может исказить результат. Например, когда я замерял, как влияет изменение яркости на потребление батареи на Nexus 5, который прослужил более года, то получил такой результат: изменение яркости экрана с 20% до 80% увеличило потребление батареи на 15% в час.
  • потребление на работу сети может существенно возрасти, если телефон движется и если плохой сотовый сигнал;
    Это связано с затратами на поиск сотовых точек. Как и в случае с яркостью экрана, надо учитывать, что это может повлиять на результаты тестов.
  • эффект от потребления на получение GPS-координат часто усиливается тем, что при обновлении координаты вызывается callback, в котором исполняется определенный объем кода.

На что же мы можем повлиять, чтоб уменьшить потребление батареи приложением? На самом деле, не на многое.
  • Прежде всего это уменьшение нагрузки на CPU. Это главный способ уменьшить расход батареи. Имеет смысл спрофилировать приложение на использование CPU. Если возможно, перенести часть нагрузки на GPU. Это может дать существенный вклад.
  • Затем сеть. Данные имеет смысл буферизировать, чтоб реже обращаться по сети. Здесь подробно описано, как уменьшить потребление батареи при передачи данных по сети.
  • Далее GPS. Если не требуется точная, часто обновляемая координата не запрашивайте её. Стоит рассмотреть возможность определения координаты по WiFi. Здесь хорошо написано про это.


Профилирование потребления батареи


Нам понадобится:
  • Android-устройство с OS 5.x и достаточно новым аккумулятором. Если это возможно, ему лучше сделать factory reset.
  • Профилировщик: github.com/google/battery-historian. Его нужно установить на десктоп и опробовать до тестов.

Каждый эксперимент состоит из следующих шагов:
  1. Полностью заряжаем батарею. Это важно, чтобы потом была возможность сравнивать результаты экспериментов между собой на одной и той же части шкалы батареи.
  2. Устанавливаем на устройство тестируемое приложение.
  3. Перезагружаем устройство. Это нужно, т.к. иначе другие приложения, запущенные в фоне, могут повлиять на результат.
  4. Сбрасываем статистику батареи: adb shell dumpsys batterystats --reset
  5. Можно также включить статистику по wakelock. По умолчанию она выключена: adb shell dumpsys batterystats --enable full-wake-history
  6. Не забываем отключить девайс от компьютера, чтобы не заряжался.
  7. Затем, запускаем тестируемое приложение.
  8. И откладываем устройство на несколько часов, так чтобы оно разрядилось как минимум на 10-15%. Во время теста желательно не трогать устройство и не включать ему экран, поскольку это может повлиять на результат.
  9. Изучаем данные за время теста.

На последнем шаге остановлюсь подробно. Изучать данные теста можно двумя способами. Можно при помощи профилировщика, разработанного Google. А можно при помощи UI в настройках Android.

1. Профилировщик battery-historian
Прежде всего, профилировщик показывает разные UI для Android 5.x и для более ранних версий ОС. Притом, количество, полнота данных и удобство отображения заметно отличаются в пользу Android 5. С другой стороны, результаты профилирования приложения на батарею могут заметно отличаться, если запускать его на разных версиях Android. Я рекомендую начинать профилирование с Android 5, а затем обязательно повторить процесс на более ранних версиях ОС.

2. UI в настройках Android
Тут все просто. На телефоне заходим в Настройки -> Батарея. Проценты означают, сколько процентов батареи потрачено на каждое приложение. За 100% принимается все, что было потрачено за время теста.



Например, экран выше показывает, что статистика собрана примерно с 22 вечера до 7:30 утра. За это время устройство разрядилось на 72% (100% — 28%). 14% от того, на что разрядилось (т.е. от 72%) ушло на экран. 12% на MAPS.ME. Если клинуть на пункт в этом списке, то можно получить детальную информацию о расходе для выбранного приложения. На этом экране также видно, что потребление резко возросло с 6 до 7 часов. Нередко бывает, что приложение активно использует Службы Google, и расход заряда списывается на них. Это также видно на скриншоте выше. Этот диалог позволяет быстро понять, что происходит с приложением на фоне остальных приложений и сервисов, запущенных системой. А в случае Android 4.x и более ранних он дает данные, которые не получить при помощи профилировщика.

Работа с профилировщиком battery-historian


Для начала профилировщик необходимо установить. Подробно процесс установки описан здесь. Он включает в себя:
  • установку go: golang.org/doc/install
    Если вы установите go, как предложено по умолчанию, в директорию $HOME/go, то достаточно задать следующие переменные окружения, после установки:
    export GOPATH=$HOME/go
    export GOBIN=$GOPATH/bin
  • установку protobuf;
  • установку профилировщика battery-historian.

Далее подключаем устройство и забираем информацию о расходе батареи с момента последнего сброса данных о заряде (он происходит, если батарея зарядилась полностью или если была вызвана команда: adb shell dumpsys batterystats --reset):

adb bugreport > bugreport.txt

Запускаем профилировщик:

cd $GOPATH/src/github.com/google/battery-historian
go run cmd/battery-historian/battery-historian.go

Заходим в браузере по адресу:
http://localhost:9999

В открывшемся окне выбираем ранее сохраненный файл bugreport.txt.

Результаты, которые вы увидите в браузере, будут зависеть от версии Android, с которой был загружен bugreport.txt. Для Android 5.x отображается страница с рядом вкладок и самой разнообразной информацией, включая статистику по каждому приложению системы. Общая статистика расхода батареи на устройстве:





Статистика по приложениям:



Для Android 4.x получим вот такой диалог. Он существенно менее информативен, но по нему можно понять, что происходит с потреблением в целом.



Режимы профилирования приложения


Имеет смысл провести проверку как минимум в двух режимах:
  • приложение на переднем плане;
  • приложение в фоне.

Желательно, чтобы приложение в фоне вообще не потребляло ресурсов, если на то нет очень веских причин. Кроме того, часто стоит отдельно проверить, как приложение потребляет батарею во время движения. Особенно, если оно использует GPS или как-то иначе связано с локацией.

Деградация аккумулятора


Важно учитывать, что со временем заряд аккумулятора слабеет. У меня был случай, когда за полгода приложение стало разряжать аккумулятор в полтора раза быстрее. Причина — старение батареи. Это стоит учитывать при тестах. Если приложение съедало всю батарею устройства за 4 часа работы полгода назад, то, скорее всего, сейчас оно будет съедать батарею за меньшее время. И причина не в том, что в приложении что-то стало не так.

Сравнение с конкурентами


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

Заключение


В этой статье я описал подходы по уменьшению потребления батареи, которые мы применяем при работе над MAPS.ME в Mail.Ru Group. Было бы интересно услышать, если кто-то использует какие-то другие средства и технологии для решения этой же задачи. Пишите в комментариях.

Комментарии (14)


  1. kolu4iy
    28.07.2015 15:02
    +1

    А кто такой UNACCOUNTED в вашем списке, пожравший 14.22% заряда (больше всех)? Как его найти?


    1. BykoIanko Автор
      28.07.2015 15:34

      Предполагаю, что UNACCOUNTED состоит из нескольких частей. Видимо, от части, это сумма всех потребителей, каждый из которых потребляет очень мало, менее чем 0.04%. Штука в том, что в списке потребителей батареи в battery historian, обычно появляются потребители, на которых ушло 0.04% и более.


  1. Revertis
    28.07.2015 16:33
    +3

    «В этой статье я описал подходы по уменьшению потребления батареи» — нет, вы описали метод анализа потребления, но не уменьшение.


    1. BykoIanko Автор
      28.07.2015 17:32

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


  1. Suncheez
    29.07.2015 03:51
    -3

    Статьи от mail.ru по информативности находятся примерно на уровне репортажей первого канала о высоких технологиях.
    У вас там за косяки отправляют писать статью на Хабр? «Ах ты нехороший человек — вовремя коммит не сделал и из-за тебя коллега простаивает! Чтобы завтра статья была на хабре!»


  1. prostosergik
    29.07.2015 11:21

    Ваш MAPS.ME, к которому вы «применяете подходы» — одно из самых жрущих приложений у меня на телефоне. Бывает, ни с того, ни с сего начинает в фоне выедать батарейку так, что доедает за 4-5 часов. Поэтому вместо нажатия кнопки «Домой» для закрытия приложения, приходится каждый раз его убивать, иначе чревато разогретым телефоном и скушанной батарейкой.

    Конечно хорошо, что вы пробуете это решить, только как-то не получается у вас.


    1. BykoIanko Автор
      29.07.2015 12:01

      На какой версии проверяли? В июньском релизе было изменение, которое должно было уменьшить потребление в фоне. Мы продолжаем работать над этим.


      1. prostosergik
        29.07.2015 12:09

        4.4.3-Google (так оно пишет свою версию), уже с новым интерфейсом а-ля Материал. Проблема, тем не менее, изредка возникает. Юзкейс: открываем приложение, смотрим местоположение, кнопка «домой» и в 1 случае из 10-15 достаем через пол-часа телефон температурой 80 градусов и в потреблении видим MAPS.ME, который скушал половину батарейки. Motorola X1060, андроид 4.4.4


        1. BykoIanko Автор
          29.07.2015 13:12

          В 4.4.3-Google уже вошло улучшение, о котором я писал. То потребление, о котором Вы пишете, у меня ни когда не воспроизводилось.

          Такое потребление означает или (1) особенность поведения приложения на конкретном железе, или (2) не точность ОС при определении приложения, на которое записывается потребление. Я постараюсь найти Motorola X1060 и проверить на нем.

          Вопросы:
          > 1 случае из 10-15 достаем через пол-часа телефон температурой 80 градусов и в потреблении видим MAPS.ME, который скушал половину батарейки.

          А можете определить, кто еще ел батарею последние полчаса, когда грелся телефон? (И какой процент на кого ушел?)

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

          Это гарантированно помогает? Или бываю случаи когда Вы остановили MAPS.ME, а девайс все равно нагрелся?


          1. prostosergik
            29.07.2015 14:06

            Знаете, не полностью я уверен что с последней версией оно повторяется, сейчас много сижу дома, а раньше ездил на мотоцикле постоянно, так что буду наблюдать, если что — сообщу в личку.

            А можете определить, кто еще ел батарею последние полчаса, когда грелся телефон? (И какой процент на кого ушел?)

            Обычно — никто. Т.е. распределение примерно такое: 45% — экран (особенности AMOLED), 30% — MAPS ME, а остальное по 3-5%

            Это гарантированно помогает? Или бываю случаи когда Вы остановили MAPS.ME, а девайс все равно нагрелся?

            Да, это гарантированно помогало и да, я точно уверен, что в этом случае перерасход батарейки останавливался.

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


            1. BykoIanko Автор
              29.07.2015 18:09

              Спасибо! Пишите конечно.
              Если будет версия с исправлениями, я дам Вам знать. Если у Вас будет возможность и найдется время её проверить — будет здорово.


              1. prostosergik
                29.07.2015 21:35

                Буду следить =)


  1. seleko
    30.07.2015 12:18
    +1

    Очень интересно! Особенно в сочетании с этим и этим.


    1. BykoIanko Автор
      30.07.2015 17:29

      Спасибо на добром слове и за ссылки!