Android - повторяющаяся задача обработчика останавливается при включенном экране в Foreground Service

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

  • Поток с "Thread.sleep" в нем (не лучший подход, но это была наша первая попытка, пошла не согласованно),
  • Поток с "Object.wait" (тот же результат, что и выше),
  • Таймер (также несовместим и прекращает выполнение через некоторое время),
  • ScheduledThreadPoolExecutor, у которого также есть проблема всего вышеупомянутого.

На моем телефоне (HUAWEI P20 Lite), попробовав эти подходы, мы наконец решили использовать Handler с HandlerThread, поскольку работа не требует взаимодействия с потоком пользовательского интерфейса. (не всегда, но мы используем отдельный обработчик для этого.) Между каждым подходом, это, кажется, наиболее последовательный, однако мы не знаем, зависит ли это от телефона, производителя или чего-либо, мой телефон перестает выполнять зацикливание обработчика с postDelayed, пока я не буду взаимодействовать с пользовательским интерфейсом приложения каким-либо образом, например: касаться уведомления, когда экран включен . Да, я не говорю о щелчке по самому уведомлению, но даже касание, или как расширение подробного текста, снова запускает обработчик. Это значит, я думаю, что мой телефон пытается сэкономить энергию, приостановив фоновое выполнение.

Теперь мы хотим запускать этот метод только при включенном экране, поскольку мы уже приостанавливаем сам обработчик с помощью "широковещательного приемника включения-выключения экрана", который зарегистрирован в службе переднего плана, удаляя обратные вызовы выполняемого объекта, но после экрана продолжается, широковещание принимается, но даже если выполняется "Handler.post()", он не запускает работающий внутри, следовательно, он никогда не зацикливается.

Чтобы дать вам немного контекста, вот какую логику мы придерживаемся:

Мы открываем сервис переднего плана с чем-то похожим на код ниже:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
    getApplicationContext().startForegroundService(
        new Intent(getApplicationContext(),
                   MyService.class));
}
else
{
    getApplicationContext().startService(
         new Intent(getApplicationContext(), MyService.class));
}

И затем внутри сервиса мы делаем что-то вроде следующего:

public int onStartCommand()
{
    // notification stuff

    HandlerThread thread = new HandlerThread("myThread");
    thread.start();

    Handler handler = new Handler(thread.getLooper());
    handler.post(new Runnable() 
    {
         // do stuff
         // this runnable stops executing after some time. This is where the problem lies.
         handler.postDelayed(this, 100);
    })

    return START_STICKY;
}

Обработчик внутри onStartCommand нестабилен, иногда он не учитывает задержку, а иногда он вообще не выполняется. Теперь, не уважая задержку, это не проблема, поскольку мы не должны быть настолько последовательными, поскольку наши вычисления не зависят от истекшего времени или чего-то еще, но если он прекращает выполнение (что в этом случае, это делает), вот где проблема начинается.

Чтобы справиться с этим, мы собираемся использовать совершенно новый класс, который предлагает Android Jetpack, WorkManager, который идеально подходит для проверки состояния обработчиков. Чтобы понять, активны ли потоки или нет, мы зарегистрировали время последнего выполнения кода внутри обработчика и собираемся получить к нему доступ через рабочий класс. Теперь, если обработчики не работают, мы хотели бы разбудить эти обработчики. Как это может быть сделано? Потому что помните, это происходит, когда экран включен.

Изменить: Вот некоторые журналы о том, как идет выполнение потока.

//2018-12-19 11:35:15.048 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:15.154 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
2018-12-19 11:35:15.262 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
2018-12-19 11:35:45.262 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:45.365 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true
//2018-12-19 11:35:45.468 17222-17952/com.example.something D/MyService: threadName: MainThread, postDelayedValue: true

Какие-либо предложения? Есть ли обходные пути для этого? Любая помощь и комментарий приветствуется, большое спасибо.

2 ответа

Решение

Обнаружил, где проблема была, он работает везде, кроме моего устройства (и, возможно, других устройств, на которых установлен EMUI), что, по-видимому, довольно строго по управлению питанием.

Вот журналы:

2018-12-19 16:45:52.943 12619-12822/com.example.something D/ThreadHelper: MainThread looping.
2018-12-19 16:45:52.986 1699-2028/? I/ash: com.example.something skips not important notification
2018-12-19 16:45:52.996 1699-2028/? I/ash: com.example.something { doze duration=40061 UptimeDuration=40061 } transition to: hibernation reason:
2018-12-19 16:45:52.996 1699-2028/? I/ash: perform hibernation actions: com.example.something

Мое устройство само по себе решает, что процесс с неважным уведомлением можно молча остановить, поэтому я повысил важность уведомления с помощью NotificationManager.IMPORTANCE_MAX. Теперь он не перестает работать вообще.

Если вы столкнулись с чем-то подобным и хотите убедиться, что вам не будет достаточно поднять уведомление, то на устройствах HUAWEI есть настройка, которую можно открыть с помощью Настройки -> Батарея -> Запуск. Отключите автоматическое управление питанием приложения и сделайте его ручным, чтобы ОС никогда не вмешивалась в процесс (надеюсь).

Это происходит на устройствах до Oreo?

Если вы начинаете службу с startForegroundServiceпозвони сразу setForeground на onStartCommand, В противном случае служба будет убита в течение короткого периода времени.

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

Другие вопросы по тегам