Comment faire en sorte que le service Android communique avec l'activité



Answers

Le demandeur est probablement passé depuis longtemps, mais au cas où quelqu'un d'autre chercherait cela ...

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

Ajoutez un BroadcastReceiver à votre activité. Enregistrez-le pour recevoir une intention personnalisée dans onResume et désenregistrez-le dans onPause. Ensuite, envoyez cette intention à votre service lorsque vous souhaitez envoyer vos mises à jour de statut ou autre.

Assurez-vous que vous ne seriez pas mécontent si une autre application écoutait votre intention (quelqu'un pourrait-il faire quelque chose de malicieux?), Mais au-delà, vous devriez vous débrouiller.

L'é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 receveur en haut de la classe:

private DataUpdateReceiver dataUpdateReceiver;

Je remplace onResume pour ajouter:

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

Et je remplace OverPause pour ajouter:

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

Maintenant, mon activité écoute mon service pour dire "Hey, allez vous mettre à jour." Je pourrais transmettre des données dans l'intention au lieu de mettre à jour les tables de base de données et ensuite revenir pour trouver les changements dans mon activité, mais comme je veux que les changements persistent, il est logique de passer les données via db.

Question

J'écris ma première application Android et j'essaie de comprendre les services et les activités. J'ai un service qui fonctionnera en tâche de fond et effectuera une journalisation basée sur le temps et le GPS. J'aurai une activité qui sera utilisée pour démarrer et arrêter le service.

Donc d'abord, je dois être capable de comprendre si le Service est en cours d'exécution lorsque l'activité est démarrée. Il y a d'autres questions à ce sujet, alors je pense que je peux comprendre cela (mais n'hésitez pas à donner des conseils).

Mon vrai problème: si l'activité est en cours et que le service est démarré, j'ai besoin d'un moyen pour le service d'envoyer des messages à l'activité. Strings simples et entiers à ce stade - messages d'état pour la plupart. Les messages ne se produiront pas régulièrement, donc je ne pense pas que l'interrogation du service est un bon moyen d'y aller s'il y a un autre moyen. Je ne souhaite cette communication que lorsque l'activité a été démarrée par l'utilisateur. Je ne souhaite pas démarrer l'activité à partir du 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 si intéressants).

Il semble que je devrais être en mesure de déterminer si le service est en cours d'exécution et, si c'est le cas, ajouter l'activité en tant qu'écouteur. Ensuite, supprimez l'activité en tant qu'écouteur lorsque l'activité s'interrompt ou s'arrête. Est-ce réellement possible? La seule façon que je peux comprendre pour le faire est de mettre en œuvre l'activité Parcelable et de construire un fichier AIDL afin que je puisse le passer à travers l'interface à distance du Service. Cela semble toutefois exagéré, et je n'ai aucune idée de la façon 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 sont intéressés par cela plus tard, il existe un exemple de code de Google pour gérer cela via AIDL dans le répertoire des exemples: /apis/app/RemoteService.java







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 messager correspondant. Cela gérera 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 passé au service en l'attachant à 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 API: MessengerService et MessengerServiceActivity . Reportez-vous à l'exemple complet de fonctionnement de MyService.




Pour suivre @MrSnowflake réponse avec un exemple de code. C'est la classe d' Application XABBER open source . La classe Application centralise et coordonne les éléments Listeners et ManagerInterfaces et plus encore. Les gestionnaires de toutes sortes sont chargés dynamiquement. Activity´s commencée dans le Xabber indiquera dans quel type d' Listener ils sont. Et quand un Service démarre le rapport dans la classe Application comme démarré. Maintenant, pour envoyer un message à une Activity tout ce que vous avez à faire est de faire en sorte que votre Activity devienne un listener du type dont vous avez besoin. Dans le OnStart() OnPause() register / unreg. Le Service peut demander à la classe Application uniquement l' listener elle a besoin et, si elle est présente, l'activité est prête à recevoir.

En parcourant la classe Application , vous verrez qu'il y a encore plus de butin.




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

Comme mentionné here dans mon blog

La communication entre le service et l'activité peut être effectuée en utilisant PendingIntent.Pour que nous puissions utiliser createPendingResult () .createPendingResult () crée un nouvel objet PendingIntent que vous pouvez remettre à service pour utiliser et renvoyer les données de résultat à votre activité dans onActivityResult (int, int, Intent) callback.Depuis qu'un PendingIntent est Parcelable, et peut donc être placé dans un 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.getStringExtra("name"),Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
}

Un service

public class PendingIntentService extends Service {

    private static final String[] 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 (String 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);

                }
            }
        }
    }
}



Related