it-roy-ru.com

Android 8.0: Java.lang.IllegalStateException: запрещено запускать службу

При запуске приложения приложение запускает службу, которая должна выполнить некоторую сетевую задачу . После нацеливания на уровень API 26 моему приложению не удается запустить службу на Android 8.0 в фоновом режиме. 

Вызывается: Java.lang.IllegalStateException: не разрешено запускать Намерение службы { CMP = my.app.tt/com.my.service }: приложение находится в фоновом режиме uid UidRecord {90372b1 u0a136 CEM бездействующие процессы: 1 сл (0,0,0)}

насколько я понимаю, это связано с: Пределы выполнения фона

Метод startService () теперь генерирует исключение IllegalStateException, если Приложение, ориентированное на Android 8.0, пытается использовать этот метод в ситуации, когда не разрешено создавать фоновые сервисы.

«в ситуации, когда это не разрешено» - что это на самом деле значит ?? И как это исправить. Я не хочу устанавливать свой сервис как "передний план"

217
phnmnn

Разрешенные ситуации - это временный белый список, в котором фоновая служба работает так же, как и до Android O.

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

  • Обработка высокоприоритетного сообщения Firebase Cloud Messaging (FCM).
  • Получение трансляции, такой как SMS/MMS-сообщение.
  • Выполнение PendingIntent из уведомления.
  • Запуск VpnService до того, как приложение VPN выходит на передний план.

Источник: https://developer.Android.com/about/versions/oreo/background.html

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

Если вы используете IntentService, вы можете перейти на JobIntentService. Смотрите @ kosev's ответ ниже .

125
Murat Karagöz

Я получил решение. Для устройств до 8.0 вы должны просто использовать startService(), но для устройств после 7.0 вы должны использовать startForgroundService(). Вот пример кода для запуска сервиса.

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

А в классе обслуживания, пожалуйста, добавьте код ниже для уведомления:

@Override
public void onCreate() {
    super.onCreate();
    startForeground(1,new Notification());
}

Где O - версия для Android 26.

Надеюсь, это решит IllegalArgumentException

140
Sagar Kacha

Лучше всего использовать JobIntentService , который использует новый JobScheduler для Oreo или старые сервисы, если они недоступны.

Объявите в своем манифесте:

<service Android:name=".YourService"
         Android:permission="Android.permission.BIND_JOB_SERVICE"/>

И в вашем сервисе вы должны заменить onHandleIntent на onHandleWork:

public class YourService extends JobIntentService {

    public static final int JOB_ID = 1;

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, YourService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // your code
    }

}

Затем вы начинаете свою службу с:

YourService.enqueueWork(context, new Intent());
45
kosev

Если служба работает в фоновом потоке с расширением IntentService, вы можете заменить IntentService на JobIntentService, который предоставляется как часть библиотеки поддержки Android

Преимущество использования JobIntentService состоит в том, что он ведет себя как IntentService на устройствах pre-O и на O и выше, он отправляет его как задание

JobScheduler также может использоваться для периодических/по требованию заданий. Но убедитесь, что вы работаете с обратной совместимостью, так как API JobScheduler доступен только из API 21

26
Harini S

Да, это потому, что вы больше не можете запускать службы в фоновом режиме через API 26. Таким образом, вы можете запустить ForegroundService выше API 26.

Вам придется использовать 

ContextCompat.startForegroundService(...)

и опубликовать уведомление при обработке утечки.

11
P.K

В заметках о выпуске firebase говорится, что поддержка Android O была впервые выпущена в 10.2.1 (хотя я бы порекомендовал использовать самую последнюю версию).

пожалуйста, добавьте новые зависимости Firebase для Android O

compile 'com.google.firebase:firebase-messaging:11.6.2'

обновить службы Google Play и Google репозитории, если это необходимо.

4
Dhaval Jivani

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

Самое простое решение - использовать новый компонент архитектуры под названием WorkManager. Вы можете проверить документацию здесь: https://developer.Android.com/topic/libraries/architecture/workmanager/

Вы просто определяете свой рабочий класс, который расширяет Worker.

public class CompressWorker extends Worker {

    public CompressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Worker.Result doWork() {

        // Do the work here--in this case, compress the stored images.
        // In this example no parameters are passed; the task is
        // assumed to be "compress the whole library."
        myCompress();

        // Indicate success or failure with your return value:
        return Result.SUCCESS;

        // (Returning RETRY tells WorkManager to try this task again
        // later; FAILURE says not to try again.)
    }
}

Затем вы планируете, когда вы хотите запустить его.

    OneTimeWorkRequest compressionWork = 
        new OneTimeWorkRequest.Builder(CompressWorker.class)
            .build();
    WorkManager.getInstance().enqueue(compressionWork);

Легко! Существует множество способов настройки рабочих. Он поддерживает повторяющиеся задания, и вы можете даже делать сложные вещи, такие как цепочка, если вам это нужно. Надеюсь это поможет.

2
TALE

Как сказал @kosev в его ответ вы можете использовать JobIntentService. Но я использую альтернативное решение - я ловлю IllegalStateException и запускаю службу в качестве переднего плана. Например, эта функция запускает мой сервис:

@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
    val context = App.context
    val intent = Intent(context, serviceType)
    intent.action = intentAction
    intentExtraSetup(intent)
    intent.putExtra(NEED_FOREGROUND_KEY, false)

    try {
        context.startService(intent)
    }
    catch (ex: IllegalStateException) {
        intent.putExtra(NEED_FOREGROUND_KEY, true)
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent)
        }
        else {
            context.startService(intent)
        }
    }
}

и когда я обрабатываю Intent, я делаю следующее:

override fun onHandleIntent(intent: Intent?) {
    val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
    if(needToMoveToForeground) {
        val notification = notificationService.createSyncServiceNotification()
        startForeground(notification.second, notification.first)

        isInForeground = true
    }

    intent?.let {
        getTask(it)?.process()
    }
}
2
Alex Shevelev

если вы встроили push-уведомление для Firebase, 

Добавьте новые/обновите зависимости сообщений Firebase для Android O (Android 8.0) из-за Пределы фонового выполнения .

compile 'com.google.firebase:firebase-messaging:11.4.0'

обновить службы Google Play и Google репозитории, если это необходимо.

Обновление:

 compile 'com.google.firebase:firebase-messaging:11.4.2'
2
Samir Mangroliya

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

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

Если выше или 8.0, то используйте это: 

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
2
Faizy

Используйте startForegroundService() вместо startService() И не забудьте создать startForeground(1,new Notification()); в вашем сервисе в течение 5 секунд после запуска сервиса.

1
Arpit

В Oreo Android определено ограничения для фоновых сервисов .

Чтобы улучшить пользовательский опыт, Android 8.0 (уровень API 26) налагает ограничения на то, что приложения могут делать во время работы в фоновом режиме.

Тем не менее, если вам нужен всегда запущенный сервис, вы можете использовать приоритетный сервис.

Фоновые ограничения службы: Пока приложение не используется, существуют ограничения к его использованию фоновых услуг. Это не относится к переднему плану услуги, которые более заметны для пользователя.

Таким образом, вы можете сделать передний план службы. Вам нужно будет показать уведомление пользователю, когда ваша служба работает. Смотрите этот ответ (Есть много других)

Решение, если -

вы не хотите получать уведомления для своего сервиса?

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

Вы можете использовать периодическое задание с Диспетчер тревог , Планировщик заданий , Evernote-Jobs или Диспетчер работ

  • Work manager - лучшее решение для периодических задач. Который был представлен с Android Architecture Component
  • В отличие от Job-Scheduler (только> 21 API) он будет работать для всех версий.
  • Также он начинает работать после режима Doze-Standby
  • Сделайте Android Boot Receiver для планирования сервиса после загрузки устройства.

Я протестировал постоянно работающий сервис с Work-Manager.

0
Khemraj

Если какое-либо намерение ранее работало нормально, когда приложение находится в фоновом режиме, это больше не будет иметь место в Android 8 и выше. Имеется в виду только намерение, которое должно выполнять некоторую обработку, когда приложение находится в фоновом режиме.

Следующие шаги должны быть выполнены:

  1. Вышеупомянутое намерение должно использовать JobIntentService вместо IntentService.
  2. Класс, который расширяет JobIntentService, должен реализовывать метод - onHandleWork(@NonNull Intent intent) и должен иметь метод ниже , Который вызовет метод onHandleWork:

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, xyz.class, 123, work);
    }
    
  3. Вызовите enqueueWork(Context, intent) из класса, в котором определены ваши намерения.

    Образец кода:

    Public class A {
    ...
    ...
        Intent intent = new Intent(Context, B.class);
        //startService(intent); 
        B.enqueueWork(Context, intent);
    }
    

Приведенный ниже класс ранее расширял класс Service

Public Class B extends JobIntentService{
...

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, B.class, JobId, work);
    }

    protected void onHandleWork(@NonNull Intent intent) {
        ...
        ...
    }
}
  1. com.Android.support:support-compat необходим для JobIntentService - я использую 26.1.0 V.

  2. Самое главное, чтобы версия библиотек Firebase была как минимум на 10.2.1, у меня были проблемы с 10.2.0 - если таковые имеются!

  3. Ваш манифест должен иметь приведенное ниже разрешение для класса Service:

    service Android:name=".B"
    Android:exported="false"
    Android:permission="Android.permission.BIND_JOB_SERVICE"
    

Надеюсь это поможет.

0
chitraju chaithanya

Я думаю, что лучший способ обойти это - проверить версию сборки во время выполнения и, в зависимости от этого, если она меньше, чем Api level 21, продолжать использовать startService (). но если оно выше, вы должны использовать планировщик заданий. Я думаю, вы можете использовать менеджер работ, но он все еще находится в стадии бета-тестирования

0
Meisam