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

Прежде всего, это длинный вопрос, нацеленный только на одну тему, и мне жаль, что я не могу поделиться каким-либо кодом, поскольку это проект нашей компании и засекречен.Мы используем сервис переднего плана, который выполняет задачу в течение 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 ]

0 голосов
/ 20 декабря 2018

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

Вотlogs:

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

0 голосов
/ 19 декабря 2018

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...