web-dev-qa-db-fra.com

Android 9.0: Interdiction de démarrer le service: l'application est en arrière-plan .. après onResume ()

J'ai un lecteur de musique qui tente de démarrer une Service dans onResume() d'un Activity. J'ai supprimé quelques lignes pour plus de clarté, mais le code est effectivement:

@Override
protected void onResume() {
    super.onResume();

    startService(new Intent(this, MusicService.class));
}

Selon les journaux des incidents, cela génère une exception sur certains appareils exécutant Android P:

Caused by Java.lang.IllegalStateException: Not allowed to start service Intent { cmp=another.music.player/com.simplecity.amp_library.playback.MusicService }: app is in background uid UidRecord{6a4a9c6 u0a143 TPSL bg:+3m25s199ms idle change:cached procs:1 seq(1283,1283,1283)}
       at Android.app.ContextImpl.startServiceCommon(ContextImpl.Java:1577)
       at Android.app.ContextImpl.startService(ContextImpl.Java:1532)
       at Android.content.ContextWrapper.startService(ContextWrapper.Java:664)
       at Android.content.ContextWrapper.startService(ContextWrapper.Java:664)
       at com.simplecity.amp_library.utils.MusicServiceConnectionUtils.bindToService(SourceFile:36)
       at com.simplecity.amp_library.ui.activities.BaseActivity.bindService(SourceFile:129)
       at com.simplecity.amp_library.ui.activities.BaseActivity.onResume(SourceFile:96)

Comment est-il possible que mon application soit en arrière-plan, immédiatement après l'appel de onResume() (et super.onResume())?

Cela n'a aucun sens pour moi. Cela pourrait-il être un bug de la plateforme? Tous les 3500+ utilisateurs affectés par cette panne sont sur Android P.

56
Tim Malseed

Il existe une solution de contournement de Google:

Le problème a été résolu dans la future version de Android.

Il existe une solution de contournement pour éviter le blocage de l'application. Les applications peuvent obtenir l'état du processus dans Activity.onResume () en appelant ActivityManager.getRunningAppProcesses () et éviter de démarrer Service si le niveau d'importance est inférieur à ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. Si le périphérique ne s’est pas complètement réveillé, les activités seront immédiatement interrompues et reprendront éventuellement après son réveil complet.

Donc je pense que ça devrait ressembler à ça:

// hack for https://issuetracker.google.com/issues/113122354
    List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
    if (runningAppProcesses != null) {
        int importance = runningAppProcesses.get(0).importance;
        // higher importance has lower number (?)
        if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
            URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this);
    }

J'ai utilisé handler comme solution de contournement et cela fonctionne plutôt bien mais pas à 100%:

// hack for https://issuetracker.google.com/issues/113122354
   handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);
6
Mateusz Kaflowski

UPDATE: Cela fonctionne pour nous dans Prod, mais ce n'est pas à 100%. J'ai reçu un rapport d'accident au cours des 18 derniers mois, alors qu'il y en aurait eu plus d'une centaine autrement. Jusqu'à ce que cela soit correctement résolu, cela semble être notre meilleure option pour le moment. Peut-être que si je levais le temps au-delà de 300, cet accident ne se serait jamais produit?

Nous testons actuellement ce qui semble fonctionner jusqu'à présent. Mettra à jour que nous voyons plus de résultats

class ResumingServiceManager(val lifecycle: Lifecycle) : LifecycleObserver {

    init {
        lifecycle.addObserver(this)
    }

    val disposable: CompositeDisposable = CompositeDisposable()

    fun startService(context: Context, intent: Intent) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            context.startService(intent)
        } else {
            Single.just(true)
                    .delaySubscription(300, TimeUnit.MILLISECONDS)
                    .subscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeBy(
                            onSuccess = {
                                context.startService(intent)
                            }

                    ).addTo(disposable)
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stopped() {
        disposable.clear()
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() {
        lifecycle.removeObserver(this)
    }
}

Dans onCreate() initialisez-le puis, chaque fois que vous souhaitez démarrer un service dans onResume, appelez simplement resumingServiceManager.startService(this, intent)

Il prend en compte le cycle de vie de sorte qu'il efface le contenu jetable s'il est mis en pause, ce qui annule le déclenchement du processus onSuccess lorsqu'il est sur le chemin de l'arrière-plan avec une ouverture/fermeture immédiate.

5
Ben987654

notre équipe confrontée au même problème. Mon calendrier indique le 5 avril 2019, mais le problème persiste toujours sur mon Samsung Galaxy S9 +, Android 9.0 (une interface utilisateur).

Nous démarrons le service dans onResume et nous lions onPause.

Comment reproduire

Verrouillez simplement votre appareil lorsque l’activité correspondant à cette logique est affichée et ne touchez pas 10 à 15 minutes. Après l'écran de déverrouillage, l'application va se bloquer.

Comment réparer

Nous avons trouvé une solution qui fonctionne réellement. Depuis Android 8+, démarrez votre service dans Android.os.Handler.post (...)

Exemple (Kotlin):

override fun onResume() {
    super.onResume()
    Handler().post {
        val serviceIntent = Intent(activity, SomeService::class.Java)
        activity?.startService(serviceIntent)
        activity?.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
    }
}

Bonne chance!

2

Ceci a été marqué comme "corrigé" dans le Android Issue Tracker:

Vraisemblablement, le correctif sera publié dans l’une des Android Q versions.

Selon le googler qui a fermé le problème,

Il existe une solution de contournement pour éviter le blocage de l'application. Les applications peuvent obtenir l'état du processus dans Activity.onResume() en appelant ActivityManager.getRunningAppProcesses() et éviter de démarrer Service si le niveau d'importance est inférieur à ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND. Si le périphérique ne s’est pas complètement réveillé, les activités seront immédiatement interrompues et reprendront éventuellement après son réveil complet.

1
Tim Malseed

Ils ont corrigé le plantage (dans une version future, pas vraiment) et ont fourni une solution de contournement pour le moment: https://issuetracker.google.com/issues/110237673#comment9

0
Dimezis