Comment faire pour que Android Service communique avec l’activité

J’écris ma première application Android et j’essaie de comprendre la communication entre les services et les activités. J’ai un service qui fonctionnera en arrière-plan et effectuera une journalisation basée sur le temps et le gps. Je vais avoir une activité qui sera utilisée pour démarrer et arrêter le service.

Il faut donc d’abord savoir si le service est en cours d’exécution au démarrage de l’activité. Il y a d’autres questions à ce sujet, alors je pense que je peux comprendre cela (mais n’hésitez pas à offrir des conseils).

Mon vrai problème: si l’activité est en cours d’exécution et que le service est démarré, j’ai besoin d’un moyen pour que le service envoie des messages à l’activité. Des chaînes simples et des entiers à ce stade – messages de statut principalement. Les messages ne se produiront pas régulièrement, donc je ne pense pas que polling the service soit un bon moyen d’y parvenir s’il y a un autre moyen. Je ne souhaite cette communication que lorsque l’activité a été lancée par l’utilisateur – je ne souhaite pas démarrer l’activité depuis le service. En d’autres termes, si vous démarrez l’activité et que le service est en cours d’exécution, vous verrez des messages d’état dans l’interface utilisateur d’activité lorsque quelque chose d’intéressant se produit. Si vous ne démarrez pas l’activité, vous ne verrez pas ces messages (ils ne sont pas intéressants).

Il semble que je devrais être en mesure de déterminer si le service est en cours d’exécution et, si tel est le cas, ajoutez l’activité en tant qu’écouteur. Supprimez ensuite l’activité en tant qu’écouteur lorsque l’activité s’interrompt ou s’arrête. Est-ce possible? La seule façon de le faire est de faire en sorte que l’Activity Activity Parcelable crée un fichier AIDL pour que je puisse le transmettre via l’interface distante du Service. Cela semble excessif, et je n’ai aucune idée de la manière dont l’activité devrait implémenter writeToParcel () / readFromParcel ().

Y a-t-il un moyen plus facile ou meilleur? Merci pour toute aide.

MODIFIER:

Pour ceux qui s’intéressent à cela plus tard, il existe un exemple de code de Google pour gérer cela via AIDL dans le répertoire samples: /apis/app/RemoteService.java

Il existe trois manières évidentes de communiquer avec les services:

  1. Utiliser les intentions
  2. Utiliser AIDL
  3. Utilisation de l’object de service lui-même (en tant que singleton)

Dans votre cas, j’irais avec l’option 3. Faites une référence statique au service lui-même et remplissez-le dans onCreate ():

void onCreate(Intent i) { sInstance = this; } 

Créez une fonction statique MyService getInstance() , qui renvoie le sInstance statique.

Ensuite, dans Activity.onCreate() vous démarrez le service, attendez de manière asynchrone jusqu’à ce que le service soit réellement démarré (vous pouvez demander à votre service de notifier votre application en envoyant une intention à l’activité.) Et obtenez son instance. Lorsque vous avez l’instance, enregistrez votre object écouteur de service auprès de votre service et vous êtes défini. REMARQUE: lorsque vous modifiez des vues dans l’activité que vous devez modifier dans le thread d’interface utilisateur, le service exécutera probablement son propre thread, vous devez donc appeler Activity.runOnUiThread() .

La dernière chose à faire est de supprimer la référence à votre object écouteur dans Activity.onPause() , faute de quoi une instance de votre contexte d’activité risque de fuir.

REMARQUE: Cette méthode n’est utile que lorsque votre application / activité / tâche est le seul processus qui accédera à votre service. Si ce n’est pas le cas, vous devez utiliser l’option 1. ou 2.

Le demandeur est probablement passé depuis longtemps, mais au cas où quelqu’un d’autre le chercherait …

Il y a une autre façon de gérer cela, ce qui pourrait être le plus simple.

Ajoutez un BroadcastReceiver à votre activité. Enregistrez-le pour recevoir des intentions personnalisées dans onResume et désinscrivez-le dans onPause. Envoyez ensuite cette intention depuis votre service lorsque vous souhaitez envoyer vos mises à jour de statut ou ce que vous avez.

Assurez-vous de ne pas être mécontent si une autre application écoutait votre intention (est-ce que quelqu’un pourrait faire quelque chose de malveillant?), Mais au-delà, vous devriez vous en sortir.

Un échantillon de code a été demandé:

Dans mon service, j’ai:

 // Do stuff that alters the content of my local SQLite Database sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT)); 

(RefreshTask.REFRESH_DATA_INTENT est juste une chaîne constante.)

Dans mon activité d’écoute, je définis mon BroadcastReceiver:

 private class DataUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(RefreshTask.REFRESH_DATA_INTENT)) { // Do stuff - maybe update my view based on the changed DB contents } } } 

Je déclare mon récepteur en haut de la classe:

 private DataUpdateReceiver dataUpdateReceiver; 

Je remplace onResume pour append:

 if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver(); IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT); registerReceiver(dataUpdateReceiver, intentFilter); 

Et je remplace onPause pour append:

 if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver); 

Maintenant, mon activité écoute mon service pour dire “Hey, va te mettre à jour”. Je pouvais transmettre des données dans Intent au lieu de mettre à jour les tables de la firebase database, puis revenir pour trouver les modifications dans mon activité, mais comme je souhaite que les modifications persistent, il est logique de transmettre les données via db.

Utilisez LocalBroadcastManager pour LocalBroadcastManager un récepteur pour écouter une diffusion envoyée depuis le service local à l’intérieur de votre application, la référence va ici:

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

Je suis surpris que personne n’ait fait référence à la bibliothèque de bus événement Otto

http://square.github.io/otto/

Je l’ai utilisé dans mes applications Android et cela fonctionne parfaitement.

L’utilisation d’un Messenger est un autre moyen simple de communiquer entre un service et une activité.

Dans l’activité, créez un gestionnaire avec un Messenger correspondant. Cela traitera les messages de votre service.

 class ResponseHandler extends Handler { @Override public void handleMessage(Message message) { Toast.makeText(this, "message from service", Toast.LENGTH_SHORT).show(); } } Messenger messenger = new Messenger(new ResponseHandler()); 

Le Messenger peut être transmis au service en le joignant à un message:

 Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER); message.replyTo = messenger; try { myService.send(message); catch (RemoteException e) { e.printStackTrace(); } 

Un exemple complet peut être trouvé dans les démos de l’API: MessengerService et MessengerServiceActivity . Reportez-vous à l’exemple complet de fonctionnement de MyService.

L’autre méthode qui n’est pas mentionnée dans les autres commentaires consiste à lier le service à partir de l’activité à l’aide de bindService () et à obtenir une instance du service dans le rappel ServiceConnection. Comme décrit ici http://developer.android.com/guide/components/bound-services.html

Une autre façon pourrait consister à utiliser des observateurs avec une classe de modèle fictive à travers l’activité et le service lui-même, en appliquant une variante de modèle MVC. Je ne sais pas si c’est la meilleure façon d’y parvenir, mais c’est la façon dont cela a fonctionné pour moi. Si vous avez besoin d’un exemple, demandez-le et je posterai quelque chose.

Pour suivre @MrSnowflake, répondez par un exemple de code. C’est la classe d’ Application XABBER maintenant open source . La classe Application centralise et coordonne les Listeners et les ManagerInterfaces, etc. Les gestionnaires de toutes sortes sont chargés dynamicment. Activity´s commencée dans Xabber indiquera dans quel type d’ Listener ils sont. Et lorsqu’un Service démarre, il est signalé à la classe d’ Application au démarrage. Maintenant, pour envoyer un message à une Activity vous suffit de faire en sorte que votre Activity devienne un type d’ listener . Dans le OnStart() OnPause() enregistrez / unreg. Le Service peut demander à la classe Application uniquement l’ listener lequel il doit parler et, si c’est là, l’activité est prête à recevoir.

En parcourant la classe Application , vous verrez qu’il y a plus de butin que cela.

Comme mentionné par Madhur, vous pouvez utiliser un bus pour la communication.

Si vous utilisez un bus, vous avez quelques options:

Otto event Bibliothèque de bus (déconseillée en faveur de RxJava)

http://square.github.io/otto/

EventBus du robot vert

http://greenrobot.org/eventbus/

NYBus (RxBus, implémenté en utilisant RxJava. Très similaire au EventBus)

https://github.com/MindorksOpenSource/NYBus

Outre LocalBroadcastManager, Event Bus et Messenger ont déjà répondu à cette question, nous pouvons utiliser l’ intention en attente pour communiquer depuis le service.

Comme mentionné ici dans mon article de blog

La communication entre le service et Activity peut être réalisée à l’aide de PendingIntent.Pour que nous puissions utiliser createPendingResult () .createPendingResult () crée un nouvel object PendingIntent que vous pouvez remettre au service et envoyer des données de résultat à onActivityResult (int, int, Intent) callback.Pendant qu’un PendingIntent est Parcelable, et peut donc être ajouté à une Intent Extra, votre activité peut transmettre ce PendingIntent au service. Le service, à son tour, peut appeler la méthode send () sur le PendingIntent pour notifier le activité via onActivityResult d’un événement.

Activité

 public class PendingIntentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PendingIntent pendingResult = createPendingResult( 100, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), PendingIntentService.class); intent.putExtra("pendingIntent", pendingResult); startService(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100 && resultCode==200) { Toast.makeText(this,data.getSsortingngExtra("name"),Toast.LENGTH_LONG).show(); } super.onActivityResult(requestCode, resultCode, data); } } 

Un service

 public class PendingIntentService extends Service { private static final Ssortingng[] items= { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; private PendingIntent data; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getParcelableExtra("pendingIntent"); new LoadWordsThread().start(); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); } class LoadWordsThread extends Thread { @Override public void run() { for (Ssortingng item : items) { if (!isInterrupted()) { Intent result = new Intent(); result.putExtra("name", item); try { data.send(PendingIntentService.this,200,result); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } SystemClock.sleep(400); } } } } }