Ainsi, avec Android O, votre service doit être exécuté en tant que service de premier plan si vous souhaitez recevoir plus que quelques mises à jour de localisation par heure.
J'ai remarqué que l'ancienne méthode de démarrage d'un service de premier plan semble fonctionner sur O .
startForeground(NOTIFICATION_ID, getNotification());
Selon le guide de changement de comportement, cliquez ici: https://developer.Android.com/preview/behavior-changes.html }
La méthode NotificationManager.startServiceInForeground () démarre un service de premier plan. L'ancienne méthode de démarrage d'un service de premier plan ne fonctionne plus.
Bien que la nouvelle méthode ne fonctionne que lorsque vous ciblez O, il semble que l'ancienne méthode semble toujours fonctionner sur un périphérique O, qu'elle cible O ou non.
Edit Exemple:
Le projet exemple Google LocationUpdatesForegroundService a en fait un exemple fonctionnel dans lequel vous pouvez voir le problème de première main . https://github.com/googlesamples/Android-play-location/tree/master/LocationUpdatesForegroundService _
La méthode startForeground semble fonctionner sans problème, que le ciblage et la compilation soient conformes au ciblage et à la compilation de niveau 25 OR de l’API (comme indiqué ici: https://developer.Android.com/preview/migration.html). #uya )
Donc, pour reproduire:
Le service est en cours d'exécution au premier plan (indiqué par l'icône dans l'ombre de la notification). Les mises à jour de localisation arrivent comme prévu (toutes les 10 secondes), même sur un appareil fonctionnant sous O. Qu'est-ce qui me manque ici?
Cela a fonctionné pour moi.
- Dans la classe d'activité, démarrez le service en utilisant startForegroundService () au lieu de startService ()
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
- Maintenant dans la classe Service dans onStartCommand () do comme suit
@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;
}
Remarque: L'utilisation de Notification.Builder au lieu de NotificationCompat.Builder l'a fait fonctionner. Seulement dans Notification.Builder, vous devrez fournir l'ID de canal, une nouvelle fonctionnalité d'Android Oreo.
Esperons que ça marche!
Dans l'activité (ou tout contexte qui démarre le service de premier plan), appelez ceci:
Intent intent = new Intent(this, MyService.class)
ContextCompat.startForegroundService(context, intent);
Une fois le service démarré, créez un canal de notification utilisant un code similaire à celui décrit par les documents Android , puis créez une version et utilisez-la:
final Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(...)//
.setPriority(...).setCategory(...).setContentTitle(...).setContentText(...).setTicker(...);
// and maybe other preparations to the notification...
startForeground(notificationId, builder.build());
Généralement, vous démarrez votre service à partir d’un récepteur de radiodiffusion en utilisant startService
. Ils disent qu'il n'est plus possible (ni plus fiable) d'appeler startService
car il y a maintenant des limitations d'arrière-plan, vous devez donc appeler startServiceInForeground
à la place. Toutefois, dans Google Documents, ce n'est pas vraiment clair, car l'application est inscrite sur la liste blanche lorsqu'elle reçoit une intention de diffusion. Il est donc difficile de savoir exactement quand startService
jette IllegalStateException
.
La méthode classique de démarrage d'un service de premier plan fonctionne toujours lorsque l'application est au premier plan, mais la méthode recommandée pour démarrer un service de premier plan pour l'API de ciblage des applications de niveau 26/Android O consiste à utiliser la méthode NotificationManager # startServiceInForeground récemment introduite en premier lieu.
L'ancienne méthode de démarrage du service en arrière-plan, puis de sa promotion au premier plan ne fonctionnera pas si l'application est en mode arrière-plan, en raison des limitations d'exécution en arrière-plan d'Android O.
Le processus de migration et les étapes sont documentés ici. https://developer.Android.com/preview/features/background.html#migration
Comme aussi @Kislingk mentionné dans un commentaire, NotificationManager.startServiceInForeground
a été supprimé. Il a été marqué comme obsolète avec le commit 08992ac .
A partir du message commit:
Plutôt que d’exiger qu’une notification a priori soit fournie pour démarrer un service directement dans l’état de premier plan, nous adoptons un processus en deux étapes opération de composé pour entreprendre des travaux de service en cours, même à partir d'un état d'exécution en arrière-plan. Le contexte # startForegroundService () n’est pas soumis à des restrictions d'arrière-plan, avec l'exigence que le service entre formellement l’état d’avant-plan via startForeground () dans 5 secondes. Si le service ne le fait pas, il est arrêté par le système d'exploitation et l'application est blâmée avec un service ANR.
startForeground (1, notification); fonctionnera sur Android O mais, selon les exigences d’Android O, nous devons montrer une notification persistante à l’utilisateur. Dans le même temps, cela peut parfois dérouter l’utilisateur (notification du système concernant le fonctionnement de l’application en arrière-plan et son impact sur la batterie) et l’utilisateur peut donc désinstaller l’application. Il serait donc préférable d'utiliser la nouvelle classe WorkManager pour planifier la tâche en avant-plan.
Extrait de code:
OneTimeWorkRequest work =
new OneTimeWorkRequest.Builder(MyWorker.class)
.build();
WorkManager.getInstance().enqueue(work);
J'ajoute un échantillon si un besoin existe avec le constructeur de pile de pile
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())