Как определить процесс переднего плана для Android O

В нашем приложении есть служба, которая обычно запускается во время Application.OnCreate (прямой вызов context.startService), а также позже через AlarmManager (выполняется рефакторинг для переноса части своей работы в JobScheduler).

В нашем приложении также есть BroadcastReceiver, который запускается с прямым намерением.

Учитывая новые ограничения в Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) возникла следующая проблема:

  • приложение/процесс находится в фоновом режиме/мертвый
  • BroadcastReceiver запускается ОС
  • Application.onCreate() выполняется перед BroadcastReceiver
  • Код Application.onCreate() пытается запустить службу

это приводит к сбою с «IllegalStateException: не разрешено запускать намерение службы».

Мне известны новые рекомендуемые способы запуска Службы, на которые ответил CommonsWare здесь https://stackoverflow.com/a/44505719/906362, но для этого конкретного случая я просто хочу иметь if(process in foreground) { startService }. В настоящее время я использую следующий метод, и он, кажется, работает:

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private static boolean isProcessInForeground_V21(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
        List<ActivityManager.AppTask> tasks = am.getAppTasks();
        return tasks.size() > 0;
    }

Но я не могу найти точные проверки, которые выполняет Android Oreo (я добрался до здесь https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java на startServiceCommon, но оттуда флаг requireForeground похоже уходит в какую-то нативную реализацию)

Итак, мой вопрос:

Для конкретной цели новых ограничений Android Oreo, как проверить, находится ли мой процесс на переднем плане перед вызовом startService?


person Budius    schedule 28.08.2017    source источник
comment
Возможно, вам не следует запускать службу в onCreate() пользовательского Application. Запускайте службу только тогда, когда она необходима. Если ваше приложение находится на переднем плане, сервис бесполезен.   -  person CommonsWare    schedule 28.08.2017


Ответы (1)


Чтобы продолжить расследование: (TL;DR: смотрите между горизонтальными линиями внизу)


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


IActivityManager определяется AIDL, поэтому для него нет исходников: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145

Основываясь на принципе реализации AIDL, я обнаружил, что ActivityManagerService extends IActivityManager.Stub (Боже, благослови Google индексация).

Примечание. Я также нашел это, что может быть интересно прочитать, если вам действительно интересно, как все работает внутри. https://programmer.group/android-9.0-source-app- процесс запуска.html

ActivityManagerService источники показывают, что в Oreo startService перенаправляется на ActiveServices, который находится в тот же пакет.

Предполагая, что мы ищем такое исключение:

java.lang.IllegalStateException: не разрешено запускать службу Intent {...}: приложение находится в фоновом режиме uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}

нам нужно продолжить путь по кроличьей норе: requireForeground назначается параметру fgRequired, и сообщение здесь. Условие, разрешающее это, зависит от режима запуска, возвращаемого ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false).

Есть 4 режима запуска:

  • APP_START_MODE_NORMAL (должно отличаться от этого, например !=)
  • APP_START_MODE_DELAYED (это нормально, т.е. return null)
  • APP_START_MODE_DELAYED_RIGID
  • APP_START_MODE_DISABLED

Эфемерные приложения немедленно вернут APP_START_MODE_DISABLED, но предполагая, что это обычное приложение, мы оказываемся в appServicesRestrictedInBackgroundLocked. Примечание. Здесь часть белого списка, упомянутого в https://stackoverflow.com/a/46445436/253468 принято решение. Поскольку все ветки, кроме последней, возвращают APP_START_MODE_NORMAL, это перенаправляет на appRestrictedInBackgroundLocked, где мы находим наш скорее всего подозреваемый:

    int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
        // Apps that target O+ are always subject to background check
        if (packageTargetSdk >= Build.VERSION_CODES.O) {
            return ActivityManager.APP_START_MODE_DELAYED_RIGID;
        }

Таким образом, причиной отказа является просто нацеливание на O. Я думаю, что окончательный ответ на ваш вопрос о том, как ОС решает, является ли ваше приложение передним или фоновым, это условие в getAppStartModeLocked

UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {

Я предполагаю, что отсутствующая запись означает, что она не запущена (но тогда как она запускает службу?!), а idle означает, что она работает в фоновом режиме. Обратите внимание, что в моем сообщении об исключении UidRecord говорится, что оно бездействует и находится в фоновом режиме в течение 3 минут 52 секунд.

Я заглянул в ваш getAppTasks, и он основан на TaskRecord.effectiveUid, так что я предполагаю, что это довольно близко к перечислению UidRecord для вашего приложения.


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

person TWiStErRob    schedule 26.04.2020