web-dev-qa-db-fra.com

L'exécution en arrière-plan n'est pas autorisée à recevoir de l'intention BOOT_COMPLETED

J'ai lu sur les limitations d'exécution en arrière-plan d'Android Oreo et il est clairement indiqué que la diffusion de BOOT_COMPLETED n'est pas affectée, mais je ne parviens pas à le faire fonctionner sous Android Oreo.

Tout d'abord, je compile avec le SDK 27. Deuxièmement, j'ai déclaré le destinataire dans le fichier manifeste:

    <uses-permission Android:name="Android.permission.RECEIVE_BOOT_COMPLETED"/>
    <receiver
        Android:name="helpers.StartDetectionAtBoot"
        Android:label="StartDetectionAtBoot"
        Android:enabled="true"
        Android:exported="true">
        <intent-filter>
            <category Android:name="Android.intent.category.DEFAULT"/>

            <action Android:name="Android.intent.action.MY_PACKAGE_REPLACED"/>

            <action Android:name="Android.intent.action.BOOT_COMPLETED"/>
            <action Android:name="Android.intent.action.QUICKBOOT_POWERON"/>
            <!--For HTC devices-->
            <action Android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            <!--For MIUI devices-->
            <action Android:name="Android.intent.action.REBOOT"/>
        </intent-filter>
    </receiver>

Ensuite, il y a l'implémentation du récepteur, qui peut aussi être simple:

public class StartDetectionAtBoot extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("test", "test");

        Intent intent0 = new Intent( context, ActivityRecognitionService.class );
        PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
        activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
    }
}

La méthode onReceive n'est pas appelée et j'obtiendrai toujours une erreur logcat sur les périphériques/émulateur Android Oreo:

W/BroadcastQueue: exécution en arrière-plan non autorisée: intention de réception { act = Android.intent.action.BOOT_COMPLETED flg = 0x400010}

En lisant d’autres réponses, ils ont dit que l’inscription d’intentions explicites dans le manifeste posait quelques problèmes, mais que ce n’est pas le cas de BOOT_COMPLETED one.

Ni ceci m'a aidé car le récepteur n'est pas appelé du tout.

Si vous enregistrez une intention de diffusion au moment de l'exécution, faites-la fonctionner (sur l'émulateur, en tirant l'intention de adb Shell), mais je ne suis pas sûr que ce soit la bonne façon de procéder:

registerReceiver(new StartDetectionAtBoot(), new IntentFilter(Intent.ACTION_BOOT_COMPLETED));

Y at-il des bugs connus avec cela?

8
fillobotto

La solution était une combinaison de deux tentatives que j'avais déjà faites.

Tout d'abord, je devais démarrer un service de premier plan (même un service factice serait une bonne chose) avec une notification persistante: 

public class StartDetectionAtBoot extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Intent intent1 = new Intent(context.getApplicationContext(), DummyService.class);
            context.startForegroundService(intent1);
        }

        Intent intent0 = new Intent( context, ActivityRecognitionService.class );
        PendingIntent pendingIntent = PendingIntent.getService(context, 111, intent0, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognitionClient activityRecognitionClient = ActivityRecognition.getClient(context);
        activityRecognitionClient.requestActivityUpdates(5000, pendingIntent);
    }
}

Bien sûr, dans le service que vous démarrez, il doit exister une méthode onCreate qui crée une notification et appelle startForeground.

Deuxièmement, j'avais invalidé le cache dans Android Studio et effacé l'instance de l'émulateur. Cette partie de la solution était nécessaire pour moi puisque la première partie ne fonctionnait toujours pas.

0
fillobotto

Auto-enregistrer votre/vos récepteur (s) dans un code pour les intentions implicites nécessaires est vraiment la bonne façon de commencer à recevoir ces intentions. Aucun service n'est nécessaire pour cela (dans la plupart des cas, voir ci-dessous ...) . Cependant, vous devez prendre en compte les éléments suivants pour éviter toute confusion lors des tests et pour ne pas interrompre une implémentation antérieure:

  1. L'enregistrement automatique doit être effectué une fois par exécution de l'application. En termes de données d'application Java/Kotlin: une fois par vie de champ statique. Donc un champ booléen statique devrait vous permettre de savoir: si vous devez vous enregistrer vous-même (par exemple, après le redémarrage ou après que votre système Android ait tué votre application plus tard ...) ou non (je cite le code de travail de ce commit: https://github.com/andstatus/todoagenda/commit/74ffc1495f2c4bebe5c43aab13389ea0ea821fde ):
    private static volatile boolean receiversRegistered = false;

    private static void registerReceivers(Context contextIn) {
        if (receiversRegistered) return;

        Context context = contextIn.getApplicationContext();
        EnvironmentChangedReceiver receiver = new EnvironmentChangedReceiver();

        IntentFilter providerChanged = new IntentFilter();
        providerChanged.addAction("Android.intent.action.PROVIDER_CHANGED");
        providerChanged.addDataScheme("content");
        providerChanged.addDataAuthority("com.Android.calendar", null);
        context.registerReceiver(receiver, providerChanged);

        IntentFilter userPresent = new IntentFilter();
        userPresent.addAction("Android.intent.action.USER_PRESENT");
        context.registerReceiver(receiver, userPresent);

        Log.i(EventAppWidgetProvider.class.getName(), "Registered receivers from " + contextIn.getClass().getName());
        receiversRegistered = true;
    }
  1. Insérez l’appel de votre méthode registerReceivers aux tous points d’entrée possibles de votre application afin de maximiser les chances que les destinataires soient enregistrés, même si votre application n’a été lancée par le système Android qu’une seule fois, par exemple:
    @Override
    public void onUpdate(Context baseContext, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        registerReceivers(baseContext);

        ...
    }
  1. Vous pouvez laisser vos destinataires enregistrés aux mêmes fins dans le fichier AndroidManifest.xml (comme cela fonctionne pour Android avant la v.7 ...), mais notez que dans ce cas, logcat affichera toujours "Exécution en arrière-plan non autorisée" avec une référence à vos destinataires. Cela signifie simplement que l'enregistrement via AndroidManifest.xml ne fonctionne pas (comme prévu pour Android 8+), mais que les destinataires auto-enregistrés doivent être appelés de toute façon!

  2. Comme je l'ai indiqué ci-dessus, le démarrage du service de premier plan n'est généralement pas nécessaire pour un widget léger. De plus, un utilisateur n'aime pas voir en permanence la notification que votre "widget" est en cours d'exécution au premier plan (et consomme donc constamment des ressources). Le seul cas où cela peut être vraiment nécessaire est lorsque Android tue votre application trop souvent et supprime ainsi l’auto-enregistrement effectué après le redémarrage .. Je pense que rendre votre "application de widget" aussi légère que possible (nécessitant aussi peu La mémoire et les ressources de processeur, dans la mesure du possible ...) constituent le moyen approprié de vous assurer que votre application de widget ne sera détruite que dans les cas critiques pour votre appareil ... Peut-être devriez-vous scinder votre grande application en deux, faisant ainsi du widget un lanceur pour l'application poids lourd qui doit fonctionner de temps en temps ...

0
yvolk