Android O - Старая служба переднего плана начала работать?

Итак, с Android O вам нужно, чтобы ваша служба работала как служба переднего плана, если вы хотите получать больше, чем несколько обновлений местоположения в час.

Я заметил, что старый метод запуска службы переднего плана, похоже, работает на O. т.е.

startForeground(NOTIFICATION_ID, getNotification());

В соответствии с инструкциями по изменению поведения здесь: https://developer.android.com/preview/behavior-changes.html

"Метод NotificationManager.startServiceInForeground() запускает службу переднего плана. Старый способ запуска службы переднего плана больше не работает."

Хотя новый метод работает только при таргетинге на O, кажется, что старый метод все еще работает на O-устройстве, будь то таргетинг O или нет. Я что-то пропустил?

//Редактировать Включая пример:

Пример проекта Google LocationUpdatesForegroundService действительно имеет рабочий пример, где вы можете увидеть проблему из первых рук. https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService

Метод startForeground работает без проблем, будь то таргетинг и компиляция по уровню API 25 или таргетинг и компиляция с O (как указано здесь: https://developer.android.com/preview/migration.html#uya)

Итак, чтобы воспроизвести:

  • Настроить приложение gradle, как указано в предыдущей ссылке
  • Откройте приложение
  • Запросить обновления местоположения
  • Закрыть приложение (через кнопку "Назад" или "Домашняя кнопка" )

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

Вот что заставляет меня думать, что я могу что-то упустить.

Ответ 1

Это сработало для меня.

  • В классе Activity запустите сервис startForegroundService() вместо startService()
    Intent myService = new Intent(this, MyService.class);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(myService);
    } else {
        startService(myService);
    }
  1. Теперь в классе Service в onStartCommand() выполните следующие действия
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    ......
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

        Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                .setContentTitle(getString(R.string.app_name))
                .setContentText(text)
                .setAutoCancel(true);

        Notification notification = builder.build();
        startForeground(1, notification);

    } else {

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText(text)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);

        Notification notification = builder.build();

        startForeground(1, notification);
    }
    return START_NOT_STICKY;
}

Примечание. Использование Notification.Builder вместо NotificationCompat.Builder заставило его работать. Только в Notification.Builder вам нужно будет предоставить идентификатор канала, который является новой функцией в Android Oreo.

Надеюсь, что это сработает!

Ответ 2

Унаследованный способ запуска службы переднего плана по-прежнему работает, когда приложение находится на переднем плане, но рекомендуемый способ запустить службу переднего плана для API-интерфейса Ориентации приложений 26/Android O - использовать новый метод NotificationManager # startServiceInForeground для создания на первом плане.

Старый способ запуска службы в фоновом режиме, а затем продвижение на передний план не будет работать, если приложение находится в фоновом режиме из-за ограничений на выполнение ОС Android.

Процесс миграции и этапы описаны здесь. https://developer.android.com/preview/features/background.html#migration

Ответ 3

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

Ответ 4

Я добавляю образец, если есть необходимость в backstack builder

    val notifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
    val playIntent    = Intent(this, this::class.java).setAction(PAUSE)
    val cancelIntent  = Intent(this, this::class.java).setAction(EXIT)

    val stop          = PendingIntent.getService(this, 1, playIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    val exit          = PendingIntent.getService(this, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)

    val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        notifyManager.createNotificationChannel(NotificationChannel(NOTIFICATION_ID_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH))
        NotificationCompat.Builder(this, NOTIFICATION_ID_CHANNEL_ID)
    } else
        NotificationCompat.Builder(this)

    builder.apply {
        setContentTitle(station.name)
        setContentText(metaToText(meta) )
        setSmallIcon(R.drawable.ic_play_arrow_white_24px)
        setAutoCancel(false)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) priority = Notification.PRIORITY_MAX
        addAction(R.drawable.ic_stop_white_24px, getString(R.string.player_notification_stop), stop)
        addAction(R.drawable.ic_close_white_24px, getString(R.string.player_notification_exit), exit)
    }

    val stackBuilder = TaskStackBuilder.create(this)
    stackBuilder.addParentStack(PlayerActivity::class.java)
    stackBuilder.addNextIntent(Intent(this, PlayerActivity::class.java))
    builder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT))

    startForeground(NOTIFICATION_ID, builder.build())

Ответ 5

Как и @Kislingk, упомянутый в комментарии NotificationManager.startServiceInForeground, был удален. Он был отмечен как устаревший с commit 08992ac.

Из сообщения о фиксации:

Вместо того, чтобы требовать, чтобы априорное уведомление было предоставлено для того, чтобы запустите услугу непосредственно в состояние переднего плана, мы примем двухступенчатую комплексная работа для выполнения текущих работ по обслуживанию даже с фоновое выполнение. Контекст # startForegroundService() не с учетом фоновых ограничений, с требованием, чтобы сервис формально входит в состояние переднего плана через startForeground() внутри 5 секунд. Если служба не делает этого, она останавливается ОС и приложение обвиняется в ANR службы.