web-dev-qa-db-fra.com

Comment mettre à jour correctement un widget dans Android 8.0 - Oreo - API 26

Supposons que j'ai un widget pour une application dont targetSDKVersion est défini sur 26. Ce widget prend entre 100 ms et 10 secondes à mettre à jour. La plupart du temps sous 1s. Avant Android O, si onUpdate () était appelé sur mon AppWidgetProvider, je pouvais lancer un service en arrière-plan pour mettre à jour ce widget. Cependant, Android O renvoie une exception IllegalStateException si vous tentez ce comportement. La solution évidente consistant à démarrer un service de premier plan semble être une mesure extrême pour quelque chose qui sera fait dans moins de 10 ans dans 99% des cas.

Solutions possibles

  • Démarrer un service de premier plan pour mettre à jour le widget. Ennuyer l'utilisateur avec une notification qui sera parti dans 10s.
  • Utilisez JobScheduler pour planifier un travail aussi rapidement que possible. Votre widget peut ne pas être mis à jour pendant un certain temps.
  • Essayez de faire le travail dans un récepteur de diffusion. Verrouillez le fil de l'interface utilisateur pour toutes les autres applications. Beurk.
  • Essayez de travailler dans le récepteur de widgets. Verrouillez le fil de l'interface utilisateur pour toutes les autres applications. Beurk.
  • Abuser de GCM pour faire fonctionner un service en arrière-plan. Beaucoup de travail et se sent hacky.

Personnellement, je n'aime aucune des solutions ci-dessus. Si tout va bien je manque quelque chose. 

(Ce qui est encore plus frustrant, c’est que mon application est déjà chargée en mémoire par le système qui appelle onUpdate (). Je ne vois pas comment charger mon application en mémoire pour appeler onUpdate (), mais je ne donnais pas alors à mon application les mises à jour du widget l'interface utilisateur sauve la vie de la batterie de n'importe qui.)

28
Justin

Vous n'indiquez pas quel est le mécanisme de déclenchement de la mise à jour. Vous semblez préoccupé par la latence ("Votre widget peut ne pas être mis à jour pendant un certain temps"), donc je vais supposer que votre préoccupation est liée à l'interaction de l'utilisateur avec le widget de l'application, telle que la pression d'un bouton.

Utilisez JobScheduler pour planifier un travail aussi rapidement que possible. Votre widget peut ne pas être mis à jour pendant un certain temps.

Il s'agit d'une variante de "use JobIntentService", selon laquelle AFAIK est la solution recommandée pour ce type de scénario.

Les autres options incluent:

  • Utilisez getForegroundService() avec PendingIntent. Avec cela, vous "jurez bien" que votre service appellera startForeground() dans le délai ANR. Si le travail dure plus de quelques secondes, appelez startForeground() pour vous assurer qu'Android ne soit pas grincheux. Cela devrait minimiser le nombre de fois où la notification de premier plan apparaît. Et, si l'utilisateur a appuyé sur un bouton et que vous êtes toujours occupé à travailler quelques secondes plus tard, vous voulez probablement afficher une notification ou faire quelque chose pour informer l'utilisateur que ce qu'il a demandé est toujours en cours.

  • Utilisez goAsync() sur BroadcastReceiver pour travailler dans le contexte du récepteur sans bloquer le thread principal de l'application. Je n'ai pas essayé avec Android 8.0+, donc YMMV.

10
CommonsWare

Une autre façon consiste à utiliser WorkManager. Oui, il reste alpha, mais Android.Arch.work:work-runtime:1.0.0-alpha10 avec targetSDKVersion défini sur 28 semble très stable à utiliser. Ainsi, dans la fonction onUpdate (), vous devez démarrer OneTimeWorkRequest qui mettra à jour ultérieurement le ou les widgets.

@Override
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) {
    super.onUpdate(context, wMgr, appWidgetIds);

    Data.Builder data_builder = new Data.Builder();
    data_builder.putIntArray(UpdaterWidgets.PARAM_WIDGET_IDS, appWidgetIds);
    OneTimeWorkRequest widgetWork =
            new OneTimeWorkRequest.Builder(UpdaterWidgets.class)
                    .setInputData(data_builder.build())
                    .build();
    WorkManager.getInstance().enqueue(widgetWork);
}
0
Alex