Зависшие частичные блокировки пробуждения

Частичная блокировка пробуждения — это механизм в API PowerManager , который позволяет разработчикам поддерживать работу ЦП после выключения дисплея устройства (будь то из-за тайм-аута системы или нажатия пользователем кнопки питания). Ваше приложение получает частичную блокировку пробуждения, вызывая acquire() с флагом PARTIAL_WAKE_LOCK . Частичная блокировка пробуждения зависает , если она удерживается в течение длительного времени, пока ваше приложение работает в фоновом режиме (никакая часть вашего приложения не видна пользователю). В этом случае разряжается аккумулятор устройства, поскольку оно не позволяет устройству переходить в режимы пониженного энергопотребления. Частичные блокировки следования следует использовать только при необходимости и отпускать, как только в них больше нет необходимости.

Если в вашем приложении зависла частичная блокировка пробуждения, вы можете использовать рекомендации на этой странице, чтобы диагностировать и устранить проблему.

Обнаружить проблему

Вы не всегда можете знать, что частичная блокировка пробуждения вашего приложения застряла. Если вы уже опубликовали свое приложение, Android Vitals может помочь вам узнать о проблеме.

Android Vitals

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

Определение сеанса батареи зависит от версии платформы.

  • В Android 10 сеанс батареи — это совокупность всех отчетов о заряде батареи, полученных в течение заданного 24-часового периода. Отчет о заряде батареи относится к интервалу между двумя зарядками батареи либо от уровня ниже 20% до выше 80%, либо от любого уровня заряда до 100%.
  • В Android 11 сеанс работы от батареи — это фиксированный 24-часовой период.

Количество отображаемых сеансов без зарядки является совокупным для всех зарегистрированных пользователей приложения. Информацию о том, как Google Play собирает данные Android Vitals, можно найти в документации Play Console .

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

Устранить проблему

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

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

Определите и исправьте места в коде, которые требуют блокировки пробуждения, например вызовы подклассов newWakeLock(int, String) или WakefulBroadcastReceiver . Вот несколько советов:

  • Мы рекомендуем включать имя вашего пакета, класса или метода в имя тега блокировки пробуждения, чтобы вы могли легко определить место в исходном коде, где была создана блокировка пробуждения. Вот несколько дополнительных советов:
    • Оставьте в имени любую личную информацию (PII), например адрес электронной почты. В противном случае устройство регистрирует _UNKNOWN вместо имени блокировки пробуждения.
    • Не получайте имя класса или метода программным путем, например, вызывая getName() , потому что Proguard может его запутать. Вместо этого используйте жестко запрограммированную строку.
    • Не добавляйте счетчик или уникальные идентификаторы в теги блокировки пробуждения. Система не сможет агрегировать блокировки пробуждения, созданные одним и тем же методом, поскольку все они имеют уникальные идентификаторы.
  • Убедитесь, что ваш код освобождает все полученные блокировки пробуждения. Это сложнее, чем гарантировать, что каждому вызовуacquire acquire() соответствует вызов release() . Вот пример блокировки пробуждения, которая не снимается из-за неперехваченного исключения:

    Котлин

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            acquire()
            doSomethingThatThrows()
            release()  // does not run if an exception is thrown
        }
    }

    Ява

        void doSomethingAndRelease() throws MyException {
            wakeLock.acquire();
            doSomethingThatThrows();
            wakeLock.release();  // does not run if an exception is thrown
        }

    Вот правильная версия кода:

    Котлин

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            try {
                acquire()
                doSomethingThatThrows()
            } finally {
                release()
            }
        }
    }

    Ява

        void doSomethingAndRelease() throws MyException {
            try {
                wakeLock.acquire();
                doSomethingThatThrows();
            } finally {
                wakeLock.release();
            }
        }
  • Убедитесь, что блокировки пробуждения снимаются, как только они больше не нужны. Например, если вы используете блокировку пробуждения, чтобы разрешить завершение фоновой задачи, убедитесь, что освобождение происходит после завершения этой задачи. Если блокировка пробуждения удерживается дольше, чем ожидалось, но не снимается, это может означать, что ваша фоновая задача занимает больше времени, чем ожидалось.

Устранив проблему в коде, убедитесь, что ваше приложение правильно снимает блокировки пробуждения, используя следующие инструменты Android:

  • dumpsys — инструмент, предоставляющий информацию о состоянии системных служб на устройстве. Чтобы просмотреть состояние службы питания, которая включает список блокировок пробуждения, запустите adb shell dumpsys power .

  • Battery Historian — инструмент, который анализирует выходные данные отчета об ошибках Android в визуальное представление событий, связанных с питанием.

Лучшие практики

В общем, вашему приложению следует избегать частичных блокировок пробуждения, поскольку это слишком легко разряжает батарею пользователя. Android предоставляет альтернативные API практически для каждого варианта использования, где раньше требовалась частичная блокировка пробуждения. Еще один вариант использования частичной блокировки пробуждения — гарантировать, что музыкальное приложение продолжает воспроизводиться, когда экран выключен. Если вы используете блокировки пробуждения для запуска задач, рассмотрите альтернативы, описанные в руководстве по фоновой обработке .

Если вам необходимо использовать частичную блокировку пробуждения, следуйте этим рекомендациям:

  • Убедитесь, что какая-то часть вашего приложения остается на переднем плане. Например, если вам нужно запустить службу, запустите вместо нее службу приоритетного плана . Это визуально указывает пользователю, что ваше приложение все еще работает.
  • Убедитесь, что логика получения и снятия блокировки блокировки максимально проста. Когда ваша логика блокировки пробуждения привязана к сложным конечным автоматам, тайм-аутам, пулам исполнителей и/или событиям обратного вызова, любая незаметная ошибка в этой логике может привести к тому, что блокировка пробуждения будет удерживаться дольше, чем ожидалось. Эти ошибки трудно диагностировать и отлаживать.
{% дословно %} {% дословно %} {% дословно %} {% дословно %}