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?
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.
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:
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;
}
@Override
public void onUpdate(Context baseContext, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
registerReceivers(baseContext);
...
}
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!
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 ...