592 votes

Exemple : Communication entre une activité et un service à l'aide de la messagerie

Je n'ai trouvé aucun exemple de la façon d'envoyer des messages entre une activité et un service, et j'ai passé beaucoup trop d'heures à le découvrir. Voici un projet d'exemple pour que d'autres puissent s'y référer.

Cet exemple vous permet de démarrer ou d'arrêter un service directement, et de vous lier/déslier séparément du service. Lorsque le service est en cours d'exécution, il incrémente un nombre à 10Hz. Si l'activité est liée au service, elle affiche la valeur actuelle. Les données sont transférées sous la forme d'un Integer et d'un String, de sorte que vous pouvez voir comment procéder de deux manières différentes. Il y a aussi des boutons dans l'activité pour envoyer des messages au service (change la valeur d'incrémentation).

Capture d'écran :

screenshot of android service messaging example

AndroidManifest.xml :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.exampleservice"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    <service android:name=".MyService"></service>
    </application>
    <uses-sdk android:minSdkVersion="8" />
</manifest> 

res \values\strings.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ExampleService</string>
    <string name="service_started">Example Service started</string> 
    <string name="service_label">Example Service Label</string> 
</resources>

res \layout\main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<RelativeLayout android:id="@+id/RelativeLayout01" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start Service"></Button>
<Button android:id="@+id/btnStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Stop Service" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

<RelativeLayout android:id="@+id/RelativeLayout02" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnBind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Bind to Service"></Button>
<Button android:id="@+id/btnUnbind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Unbind from Service" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
<TextView android:id="@+id/textIntValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Integer Value Goes Here" />
<TextView android:id="@+id/textStrValue" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="String Value Goes Here" />

<RelativeLayout android:id="@+id/RelativeLayout03" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:id="@+id/btnUpby1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Increment by 1"></Button>
<Button android:id="@+id/btnUpby10" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Increment by 10" android:layout_alignParentRight="true"></Button>
</RelativeLayout>

</LinearLayout>

src \com.exampleservice\MainActivity.java :

package com.exampleservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    Button btnStart, btnStop, btnBind, btnUnbind, btnUpby1, btnUpby10;
    TextView textStatus, textIntValue, textStrValue;
    Messenger mService = null;
    boolean mIsBound;
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyService.MSG_SET_INT_VALUE:
                textIntValue.setText("Int Message: " + msg.arg1);
                break;
            case MyService.MSG_SET_STRING_VALUE:
                String str1 = msg.getData().getString("str1");
                textStrValue.setText("Str Message: " + str1);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            textStatus.setText("Attached.");
            try {
                Message msg = Message.obtain(null, MyService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even do anything with it
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
            mService = null;
            textStatus.setText("Disconnected.");
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnStart = (Button)findViewById(R.id.btnStart);
        btnStop = (Button)findViewById(R.id.btnStop);
        btnBind = (Button)findViewById(R.id.btnBind);
        btnUnbind = (Button)findViewById(R.id.btnUnbind);
        textStatus = (TextView)findViewById(R.id.textStatus);
        textIntValue = (TextView)findViewById(R.id.textIntValue);
        textStrValue = (TextView)findViewById(R.id.textStrValue);
        btnUpby1 = (Button)findViewById(R.id.btnUpby1);
        btnUpby10 = (Button)findViewById(R.id.btnUpby10);

        btnStart.setOnClickListener(btnStartListener);
        btnStop.setOnClickListener(btnStopListener);
        btnBind.setOnClickListener(btnBindListener);
        btnUnbind.setOnClickListener(btnUnbindListener);
        btnUpby1.setOnClickListener(btnUpby1Listener);
        btnUpby10.setOnClickListener(btnUpby10Listener);

        restoreMe(savedInstanceState);

        CheckIfServiceIsRunning();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("textStatus", textStatus.getText().toString());
        outState.putString("textIntValue", textIntValue.getText().toString());
        outState.putString("textStrValue", textStrValue.getText().toString());
    }
    private void restoreMe(Bundle state) {
        if (state!=null) {
            textStatus.setText(state.getString("textStatus"));
            textIntValue.setText(state.getString("textIntValue"));
            textStrValue.setText(state.getString("textStrValue"));
        }
    }
    private void CheckIfServiceIsRunning() {
        //If the service is running when the activity starts, we want to automatically bind to it.
        if (MyService.isRunning()) {
            doBindService();
        }
    }

    private OnClickListener btnStartListener = new OnClickListener() {
        public void onClick(View v){
            startService(new Intent(MainActivity.this, MyService.class));
        }
    };
    private OnClickListener btnStopListener = new OnClickListener() {
        public void onClick(View v){
            doUnbindService();
            stopService(new Intent(MainActivity.this, MyService.class));
        }
    };
    private OnClickListener btnBindListener = new OnClickListener() {
        public void onClick(View v){
            doBindService();
        }
    };
    private OnClickListener btnUnbindListener = new OnClickListener() {
        public void onClick(View v){
            doUnbindService();
        }
    };
    private OnClickListener btnUpby1Listener = new OnClickListener() {
        public void onClick(View v){
            sendMessageToService(1);
        }
    };
    private OnClickListener btnUpby10Listener = new OnClickListener() {
        public void onClick(View v){
            sendMessageToService(10);
        }
    };
    private void sendMessageToService(int intvaluetosend) {
        if (mIsBound) {
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_SET_INT_VALUE, intvaluetosend, 0);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                }
            }
        }
    }

    void doBindService() {
        bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
        textStatus.setText("Binding.");
    }
    void doUnbindService() {
        if (mIsBound) {
            // If we have received the service, and hence registered with it, then now is the time to unregister.
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MyService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    // There is nothing special we need to do if the service has crashed.
                }
            }
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
            textStatus.setText("Unbinding.");
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            doUnbindService();
        } catch (Throwable t) {
            Log.e("MainActivity", "Failed to unbind from the service", t);
        }
    }
}

src \com.exampleservice\MyService.java :

package com.exampleservice;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
    private NotificationManager nm;
    private Timer timer = new Timer();
    private int counter = 0, incrementby = 1;
    private static boolean isRunning = false;

    ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
    int mValue = 0; // Holds last value set by a client.
    static final int MSG_REGISTER_CLIENT = 1;
    static final int MSG_UNREGISTER_CLIENT = 2;
    static final int MSG_SET_INT_VALUE = 3;
    static final int MSG_SET_STRING_VALUE = 4;
    final Messenger mMessenger = new Messenger(new IncomingHandler()); // Target we publish for clients to send messages to IncomingHandler.

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
    class IncomingHandler extends Handler { // Handler of incoming messages from clients.
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_REGISTER_CLIENT:
                mClients.add(msg.replyTo);
                break;
            case MSG_UNREGISTER_CLIENT:
                mClients.remove(msg.replyTo);
                break;
            case MSG_SET_INT_VALUE:
                incrementby = msg.arg1;
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
    private void sendMessageToUI(int intvaluetosend) {
        for (int i=mClients.size()-1; i>=0; i--) {
            try {
                // Send data as an Integer
                mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, intvaluetosend, 0));

                //Send data as a String
                Bundle b = new Bundle();
                b.putString("str1", "ab" + intvaluetosend + "cd");
                Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
                msg.setData(b);
                mClients.get(i).send(msg);

            } catch (RemoteException e) {
                // The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
                mClients.remove(i);
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("MyService", "Service Started.");
        showNotification();
        timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 0, 100L);
        isRunning = true;
    }
    private void showNotification() {
        nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.service_started);
        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
        // Send the notification.
        // We use a layout id because it is a unique number.  We use it later to cancel.
        nm.notify(R.string.service_started, notification);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyService", "Received start id " + startId + ": " + intent);
        return START_STICKY; // run until explicitly stopped.
    }

    public static boolean isRunning()
    {
        return isRunning;
    }

    private void onTimerTick() {
        Log.i("TimerTick", "Timer doing work." + counter);
        try {
            counter += incrementby;
            sendMessageToUI(counter);

        } catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
            Log.e("TimerTick", "Timer Tick Failed.", t);            
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (timer != null) {timer.cancel();}
        counter=0;
        nm.cancel(R.string.service_started); // Cancel the persistent notification.
        Log.i("MyService", "Service Stopped.");
        isRunning = false;
    }
}

0 votes

Je pense que cela vous aidera ---------- stackoverflow.com/questions/1464853/

0 votes

Si vous n'avez pas besoin d'envoyer beaucoup de données, vous pouvez utiliser la fonction Intentions .

0 votes

Ou Diffusions en général !

47voto

Christopher Orr Points 58514

Regardez le Exemple de LocalService .

Votre Service renvoie une instance de lui-même aux consommateurs qui appellent onBind . Ensuite, vous pouvez interagir directement avec le service, par exemple en enregistrant votre propre interface d'écoute avec le service, afin de recevoir des rappels.

2 votes

Le seul problème, c'est qu'il n'utiliserait pas un Messenger et ne répondrait donc pas à cette pseudo-question. J'ai utilisé un LocalService, mais j'ai été heureux de trouver un exemple de Messenger/Handler. Je ne crois pas qu'un LocalService puisse être placé dans un autre processus.

0 votes

@Christoper-Orr : Je suis très reconnaissant que vous ayez posté ce lien de la Android BroadcastReceiver tutoriel. J'ai utilisé un LocalBroadcastManager pour échanger en permanence des données entre deux Activity instances.

0 votes

Le problème avec LocalBroadcastManager est qu'elle n'est pas bloquante et que vous devez attendre les résultats. Parfois, vous voulez des résultats immédiats.

20voto

SciJoker Points 258

Pour envoyer des données au service, vous pouvez utiliser :

Intent intent = new Intent(getApplicationContext(), YourService.class);
intent.putExtra("SomeData","ItValue");
startService(intent);

Et après dans le service dans onStartCommand() obtenir des données de l'intention

Pour envoyer des données ou un événement du service à l'application (pour une ou plusieurs activités) :

 private void sendBroadcastMessage(String intentFilterName, int arg1, String extraKey) {
        Intent intent = new Intent(intentFilterName);
        if (arg1 != -1 && extraKey != null) {
            intent.putExtra(extraKey, arg1);
        }
        sendBroadcast(intent);
    }

Cette méthode est un appel de votre service. Vous pouvez simplement envoyer des données pour votre activité

private void someTaskInYourService(){
//for example you downloading from server 1000 files
    for(int i=0;i<1000;i++){
Thread.sleep(5000) // 5 second. Catch in try-catch block 
sendBroadCastMessage(Events.UPDATE_DOWNLOADING_PROGRESSBAR, i,0,"up_download_progress");
}

Pour recevoir un événement avec des données, veuillez créer et enregistrer dans votre activité la méthode registerBroadcastReceivers() :

  private void registerBroadcastReceivers(){
        broadcastReceiver = new BroadcastReceiver() {
                    @Override
                    public void onReceive(Context context, Intent intent) {
                     int arg1 = intent.getIntExtra("up_download_progress",0);
progressBar.setProgress(arg1);
                       }};
         IntentFilter progressfilter = new IntentFilter(Events.UPDATE_DOWNLOADING_PROGRESS);
                registerReceiver(broadcastReceiver,progressfilter);

Pour envoyer plus de données, vous pouvez modifier la méthode sendBroadcastMessage() ; N'oubliez pas : vous devez enregistrer les diffusions dans les méthodes onResume() et les désenregistrer dans onStop() !

2 votes

Où dois-je enregistrer le récepteur dans onResume et dans onStop ? Est-ce que je dois le faire dans l'activité ?

0 votes

Oui. Pour recevoir un événement du service, vous devez enregistrer la diffusion dans onResume. Rappelez-vous que vous devez désenregistrer la diffusion dans onStop. Maintenant, je ne recommande pas d'utiliser ma méthode. Veuillez utiliser des librairies spéciales pour communiquer avec d'autres vues/activités/services comme EventBus. github.com/greenrobot/EventBus ou Otto github.com/square/otto

1 votes

Pouvez-vous m'aider à savoir comment je peux utiliser ceci, je suis bloqué dans mon projet de communication de service.

14voto

Someone Somewhere Points 8361

note : vous n'avez pas besoin de vérifier si votre service est en cours d'exécution, CheckIfServiceIsRunning() parce que bindService() le fera démarrer s'il n'est pas en marche.

aussi : si vous faites pivoter le téléphone, vous ne voulez pas qu'il bindService() encore une fois parce que onCreate() sera appelé à nouveau. Veillez à définir onConfigurationChanged() pour éviter cela.

0 votes

Dans mon cas, je n'ai pas besoin que le service fonctionne en permanence. Si le service est déjà en cours d'exécution lorsque l'activité démarre, alors je veux m'y lier. Si le service n'est pas en cours d'exécution lorsque l'activité démarre, je veux laisser le service arrêté.

1 votes

Je ne suis pas sûr que ce soit vrai, bindService ne démarre pas le service, pouvez-vous indiquer la documentation ?

1 votes

developer.Android.com/reference/Android/app/Service.html Le premier paragraphe indique Services can be started with Context.startService() and Context.bindService()

8voto

Ishank Gupta Points 146

Tout va bien Bon exemple de communication activité/service en utilisant Messenger. Un commentaire : la méthode MyService.isRunning() n'est pas nécessaire. bindService() peut être fait n'importe quel nombre de fois. aucun mal à cela.

Si MyService est exécuté dans un autre processus, la fonction statique MyService.isRunning() renverra toujours false. Il n'est donc pas nécessaire d'utiliser cette fonction.

4voto

binwiederhier Points 319

J'ai utilisé votre code pour créer un moyen très générique de communiquer entre un service personnalisé et une activité. Si quelqu'un est intéressé, regardez le article de blog ou le code complet sur Launchpad .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X