368 votes

Context.startForegroundService () n'a pas alors appelé Service.startForeground ()

J'utilise Service Class sur le système d'exploitation Android O.

Je prévois d'utiliser les Service en arrière-plan.

La recommandation Android indique que startService() devrait utiliser startForegroundService() .

Si vous utilisez startForegroundService() , le Service jette

Context.startForegroundService () n'a pas alors appelé Service.startForeground ()

Erreur.

Quel est le problème avec cela?

139voto

zhuzhumouse Points 759

À partir de Google docs sur Android 8.0 changements de comportement:

Le système permet aux applications de Contexte d'appel.startForegroundService() même lorsque l'application est en arrière-plan. Cependant, l'application doit appeler ce service de startForeground() la méthode dans les cinq secondes après que le service est créé.

Solution: Appelez startForeground() en onCreate() de la Service qui vous l'utilisez, Context.startForegroundService()

Voir aussi: Exécution en arrière-plan des Limites pour Android 8.0 (Oreo)

121voto

humazed Points 398

J'ai appelé ContextCompat.startForegroundService(this, intent) pour démarrer le service puis

En service onCreate

  @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}
 

81voto

ZhouX Points 492

Pourquoi cette question est-ce qui se passe est parce que Android-cadre ne peut pas garantir votre service de commencer un délai de 5 secondes mais d'un autre côté cadre de la stricte limite sur le premier plan, la notification doit être licencié dans un délai de 5 secondes, sans vérifier si le cadre avait essayé de démarrer le service.

C'est certainement une question cadre, mais pas tous les développeurs face à cette problématique qui font de leur mieux:

  1. startForeground une notification doit être faite dans les deux onCreate et onStartCommand, parce que si votre service est déjà créé, et en quelque sorte votre activité est en essayant de le démarrer à nouveau, onCreate ne sera pas appelé.

  2. ID de notification ne doit pas être 0 sinon même incident se produit même c'est pas la même raison.

  3. stopSelf ne doit pas être appelée avant d' startForeground.

Avec tous au-dessus de 3 cette question peut être réduit un peu, mais toujours pas une solution, la vraie solution, ou disons que la solution de contournement consiste à rétrograder votre cible sdk de la version à 25.

Et notez que le plus probable Android P sera encore porter cette question parce que Google refuse à même de comprendre ce qui se passe et ne crois pas que c'est de leur faute, lire #36 et #56 pour plus d'informations

46voto

swooby Points 437

Votre application va planter si vous appelez Context.startForegroundService(...) puis appelez Context.stopService(...) avant Service.startForeground(...) est appelé.

J'ai une nette repro ici ForegroundServiceAPI26

J'ai ouvert un bug sur ce sujet à : Google issue tracker

Plusieurs bugs sur ce ont été ouverts et fermés ne sera pas Corrigé.

Espérons que le mien avec claire étapes de reproduction va faire la coupe.

Les informations fournies par l'équipe de google

Google issue tracker Commentaire 36

Ce n'est pas un cadre de bug; c'est intentionnel. Si l'application démarre une instance de service avec startForegroundService(), il doit la transition de cette instance de service à l'avant-plan de l'état et de montrer la notification. Si l'instance de service est arrêté avant d' startForeground() est appelé sur elle, cette promesse est le néant: c'est un bug dans l'application.

Re #31, la publication d'un Service que d'autres applications puissent démarrer directement est fondamentalement dangereux. Vous pouvez atténuer un peu par le traitement de toutes les actions de démarrage de ce service comme nécessitant startForeground(), bien évidemment, cela peut ne pas être ce que vous aviez à l'esprit.

Google issue tracker Commentaire 56

Il ya un couple de différents scénarios qui conduisent au même résultat ici.

Pure sémantique problème, que c'est tout simplement une erreur à coup quelque chose avec startForegroundService() mais en négligeant de fait transition à premier plan par startForeground(), c'est juste que: une question sémantique. Qui est considéré comme une application bug, intentionnellement. L'arrêt du service avant de faire la transition vers le premier plan est une application d'erreur. C'est là le fond de l'OP, et c'est pourquoi cette question a été marquée "fonctionne comme prévu."

Cependant, il y a aussi des questions sur les fausses détection de ce problème. Qui est considérée comme un véritable problème, si elle était suivie séparément à partir de ce bug tracker question. Nous ne sommes pas sourds à la plainte.

24voto

Ahmad Arslan Points 2450

J'ai fait des recherches à ce sujet pendant quelques jours et j'ai trouvé la solution. Maintenant, dans Android O, vous pouvez définir la limitation d'arrière-plan comme ci-dessous

Le service qui appelle une classe de service

 Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){

                        SettingActivity.this.startForegroundService(serviceIntent);
                    }else{
                        startService(serviceIntent);
                    }
 

et la classe de service devrait être comme

 public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X