Android 8.0: java.ленг.IllegalStateException: не допускается, чтобы начать обслуживание намерениях


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

вызвано: java.ленг.IllegalStateException: не разрешено запускать намерение обслуживания { СМР=мой.приложение.ТТ/сом.мой.услуги }: приложение находится в фоновом режиме uid UidRecord{90372b1 u0a136 CEM idle procs:1 seq (0,0,0)}

Как я понимаю, это связано к: ограничения фонового выполнения

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

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

12 155

12 ответов:

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

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

  • работа с высоким приоритетом облако опорного пункта обмена сообщениями (сообщение ФКМ).
  • получение широковещательной передачи, например SMS / MMS сообщения.
  • выполнение PendingIntent из уведомления.
  • запуск VpnService до того, как приложение VPN продвигается на передний план.

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

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

у меня есть решение. Для устройств до 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

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

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

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

лучший способ-использовать 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());

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

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

ContextCompat.startForegroundService(...)

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

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

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

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

при необходимости обновите сервисы google play и репозитории google.

Если вы интегрировали Firebase messaging push notification, то

добавить новые / обновить 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'

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

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

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

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
        ?.isIgnoringBatteryOptimizations(packageName) != false) {
    startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash

Если вы используете свой код на 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 );

- первый метод вызова в стартовом приложении или основной деятельности:

public static void createChancelNotification(Context mContext) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationManager notificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel androidChannel = new NotificationChannel(Constants.ANDROID_CHANNEL_ID,
                Constants.ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_MAX);
        notificationManager.createNotificationChannel(androidChannel);
    }
}

- второй, когда закрыть приложение и firebase push:

public class FirebaseDataReceiver extends WakefulBroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getExtras() != null) {
        for (String key : intent.getExtras().keySet()) {
            Object value = intent.getExtras().get(key);
            Log.e("FirebaseDataReceiver", "Key: " + key + " Value: " + value);
        }
    }
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        // Attach component of GCMIntentService that will handle the intent in background thread
        ComponentName componentName = new ComponentName(context.getPackageName(), MyFirebaseMessageService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, intent.setComponent(componentName));
        setResultCode(Activity.RESULT_OK);
    }

}

}

- последний дескриптор в файл:

public class MyFirebaseMessageService extends FirebaseMessagingService {
private static final String TAG = MyFirebaseMessageService.class.getSimpleName();

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (remoteMessage.getData() != null) {
        // Do some thing here
    }
}

не использовать в onStartCommand:

return START_NOT_STICKY

просто измените его на:

return START_STICKY

и это будет работать