web-dev-qa-db-fra.com

Communication entre activité et service

J'essaie de créer mon propre MusicPlayer pour Android. Là où j’ai eu un problème, j’exécute certaines choses en arrière-plan. L’activité principale gère l’interface graphique et jusqu’à présent toutes les chansons sont en cours de lecture. Je voulais séparer l'interface utilisateur graphique et la musique en cours. Je veux mettre la gestion de la musique dans Service et laisser les autres choses telles qu’elles sont maintenant. 

Mon problème est que je ne peux pas organiser la communication entre l'activité et le service car beaucoup de communication se passe entre eux, y compris des objets en mouvement dans les deux sens. J'ai essayé de nombreuses techniques que j'ai recherchées ici sur Stack Overflow mais à chaque fois que j'ai eu des problèmes. J'ai besoin de Service pour pouvoir envoyer des objets à Activity et vice versa. Lorsque j'ajoute un widget, je souhaite également qu'il puisse communiquer avec le service.

Tous les conseils sont appréciés, si vous avez besoin du commentaire de code source ci-dessous, mais maintenant, dans cette transition, il est devenu chaotique.

Existe-t-il un didacticiel plus avancé que l’appel d’une méthode renvoyant un nombre aléatoire du service? : P 

EDIT: la solution possible consiste à utiliser la bibliothèque RoboGuice et à déplacer des objets avec injection

48
Dejan

J'ai mis en place une communication entre Activity et Service à l'aide des interfaces Bind et Callbacks. 

Pour envoyer des données au service, j'ai utilisé Binder qui renvoie l'instance de service à l'activité, puis l'activité peut accéder aux méthodes publiques du service. 

Pour renvoyer des données à l’activité depuis le service, j’ai utilisé l’interface de rappel comme vous l’utilisez lorsque vous souhaitez communiquer entre Fragment et Activity. 

Voici quelques exemples de code pour chacun: L'exemple suivant illustre la relation bidirectionnelle Activité et Service: L'activité comporte 2 boutons: Le premier bouton permet de démarrer et d'arrêter le service . Le deuxième bouton démarrer une minuterie qui s'exécute dans le service. 

Le service mettra à jour l'activité via un rappel avec la progression du minuteur. 

Mon activité:

    //Activity implements the Callbacks interface which defined in the Service  
    public class MainActivity extends ActionBarActivity implements MyService.Callbacks{

    ToggleButton toggleButton;
    ToggleButton tbStartTask;
    TextView tvServiceState;
    TextView tvServiceOutput;
    Intent serviceIntent;
    MyService myService;
    int seconds;
    int minutes;
    int hours;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         serviceIntent = new Intent(MainActivity.this, MyService.class);
        setViewsWidgets();
    }

    private void setViewsWidgets() {
        toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
        toggleButton.setOnClickListener(btListener);
        tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
        tbStartTask.setOnClickListener(btListener);
        tvServiceState = (TextView)findViewById(R.id.tvServiceState);
        tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);

    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
            // We've binded to LocalService, cast the IBinder and get LocalService instance
            MyService.LocalBinder binder = (MyService.LocalBinder) service; 
            myService = binder.getServiceInstance(); //Get instance of your service! 
            myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks! 
            tvServiceState.setText("Connected to service...");
            tbStartTask.setEnabled(true);
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
            tvServiceState.setText("Service disconnected");
            tbStartTask.setEnabled(false);
        }
    };

    View.OnClickListener btListener =  new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(v == toggleButton){
                if(toggleButton.isChecked()){
                    startService(serviceIntent); //Starting the service 
                    bindService(serviceIntent, mConnection,            Context.BIND_AUTO_CREATE); //Binding to the service! 
                    Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
                }else{
                    unbindService(mConnection);
                    stopService(serviceIntent);
                    Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
                    tvServiceState.setText("Service disconnected");
                    tbStartTask.setEnabled(false);
                }
            }

            if(v == tbStartTask){
                if(tbStartTask.isChecked()){
                      myService.startCounter();
                }else{
                    myService.stopCounter();
                }
            }
        }
    };

    @Override
    public void updateClient(long millis) {
        seconds = (int) (millis / 1000) % 60 ;
        minutes = (int) ((millis / (1000*60)) % 60);
        hours   = (int) ((millis / (1000*60*60)) % 24);

        tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) :  String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
    }
}

Et voici le service:

 public class MyService extends Service {
    NotificationManager notificationManager;
    NotificationCompat.Builder mBuilder;
    Callbacks activity;
    private long startTime = 0;
    private long millis = 0;
    private final IBinder mBinder = new LocalBinder();
    Handler handler = new Handler();
    Runnable serviceRunnable = new Runnable() {
        @Override
        public void run() {
            millis = System.currentTimeMillis() - startTime;
            activity.updateClient(millis); //Update Activity (client) by the implementd callback
            handler.postDelayed(this, 1000);
        }
    };


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        //Do what you need in onStartCommand when service has been started
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //returns the instance of the service
    public class LocalBinder extends Binder{
        public MyService getServiceInstance(){
            return MyService.this;
        }
    }

    //Here Activity register to the service as Callbacks client
    public void registerClient(Activity activity){
        this.activity = (Callbacks)activity;
    }

    public void startCounter(){
        startTime = System.currentTimeMillis();
        handler.postDelayed(serviceRunnable, 0);
        Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
    }

    public void stopCounter(){
        handler.removeCallbacks(serviceRunnable);
    }


    //callbacks interface for communication with service clients! 
    public interface Callbacks{
        public void updateClient(long data);
    }
}
66
Moti Bartov

Mise à jour: 10 juillet 2016

OMI Je pense que l’utilisation de BroadcastReceiver pour des événements personnalisés est un meilleur moyen comme les messagers mentionnés ne gèrent pas la récréation d'activité sur l'appareil rotation ainsi que les éventuelles fuites de mémoire.

Vous pouvez créer un récepteur BroadCast personnalisé pour les événements de l'activité. Vous pouvez également utiliser Messengers.

  1. Dans votre Activity 

    créer une classe MessageHandler en tant que 

    public static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
            int state = message.arg1;
            switch (state) {
            case HIDE:
                progressBar.setVisibility(View.GONE);
                break;
            case SHOW:
                progressBar.setVisibility(View.VISIBLE);
                break;
            }
        }
    }
    

    Maintenant, vous pouvez avoir son exemple 

    public static Handler messageHandler = new MessageHandler();
    

    Commencez votre Service avec cet objet Handler en tant que données supplémentaires en tant que 

    Intent startService = new Intent(context, SERVICE.class)
    startService.putExtra("MESSENGER", new Messenger(messageHandler));
    context.startService(startService);
    
  2. Dans votre Service, vous recevez cet objet de l'intention et initialisez la variable Messenger dans Service en tant que

    private Messenger messageHandler;
    Bundle extras = intent.getExtras();
    messageHandler = (Messenger) extras.get("MESSENGER");
    sendMessage(ProgressBarState.SHOW);
    

    Et écrivez ensuite une méthode sendMessage pour envoyer des messages à activity.

    public void sendMessage(ProgressBarState state) {
    Message message = Message.obtain();
    switch (state) {
        case SHOW :
            message.arg1 = Home.SHOW;
            break;
        case HIDE :
            message.arg1 = Home.HIDE;
            break;
    }
    try {
        messageHandler.send(message);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
    }
    

L'exemple de code ci-dessus affiche et masque une barre de progression dans l'activité lorsque les messages sont reçus du service.

54
Rachit Mishra

Les intentions sont une bonne solution pour la communication entre l’Activité et le Service.

Une solution rapide pour recevoir les intentions de votre service consiste à sous-classer IntentService class. Il traite les demandes asynchrones exprimées en tant qu'intentions à l'aide d'une file d'attente et d'un thread de travail.

Pour la communication de service à activité, vous pouvez diffuser l'intention, mais au lieu d'utiliser sendBroadcast () normal de Context, une méthode plus efficace consiste à utiliser LocalBroadcastManager depuis la bibliothèque de support.

Exemple de service. 

public class MyIntentService extends IntentService {
    private static final String ACTION_FOO = "com.myapp.action.FOO";
    private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";

    public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
    public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";

    // called by activity to communicate to service
    public static void startActionFoo(Context context, String param1) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        context.startService(intent);
    }

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
                // do something
            }
        }
    }

    // called to send data to Activity
    public static void broadcastActionBaz(String param) {
        Intent intent = new Intent(BROADCAST_ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM_B, param);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.sendBroadcast(intent);
    }
}

Exemple d'activité

public class MainActivity extends ActionBarActivity {

    // handler for received data from service
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
                final String param = intent.getStringExtra(EXTRA_PARAM_B);
                // do something
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter filter = new IntentFilter();
        filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.registerReceiver(mBroadcastReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
        bm.unregisterReceiver(mBroadcastReceiver);
        super.onDestroy();
    }

    // send data to MyService
    protected void communicateToService(String parameter) {
        MyIntentService.startActionFoo(this, parameter);
    }
}
14
mpolci

Je pense qu'il y a un problème avec la bonne réponse. Je n'ai pas assez de réputation pour commenter.

Droit dans la réponse: Activité appelez bindService () pour obtenir le pointeur sur le service est ok. Parce que le contexte de service est maintenu lorsque la connexion est maintenue.

Faux dans la réponse: Le pointeur de service sur la classe d'activité à rappeler est un mauvais moyen. L'instance d'activité n'est peut-être pas nulle pendant le contexte d'activité. Version => exception ici.

solution pour le mauvais dans la réponse: service envoyer l'intention à l'activité. et l'intention du récepteur d'activité via BroadcastReceiver.

Remarque: Dans ce cas, Service et Activité dans le même processus, vous devez utiliser LocalBroadcastManager pour envoyer une intention. Cela améliore les performances et la sécurité

5
HungNM2

Le meilleur moyen dans ce cas est de communiquer en diffusant à partir de votre service pour différentes actions et en le recevant dans votre activité. Vous pouvez créer une diffusion personnalisée et envoyer des codes définissant des événements spécifiques tels que compléter, modifier, préparer, etc.

2
Vikram

Ceci est un exemple simple de communication entre activité et service

Activité

MyReceiver myReceiver; //my global var receiver
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layourAwesomexD);
    registerReceiver();
}

//When the activity resume, the receiver is going to register...
@Override
protected void onResume() {
    super.onResume();
    checkStatusService(); // verficarStatusServicio(); <- name change
    registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
@Override
protected void onStop() {
    unregisterReceiver(myReceiver); //unregister my receiver...
    super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
    //Register BroadcastReceiver
    //to receive event from our service
    myReceiver = new MyReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(MyService.SENDMESAGGE);
    registerReceiver(myReceiver, intentFilter);
}

// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context arg0, Intent arg1) {
        //verify if the extra var exist
        System.out.println(arg1.hasExtra("message")); // true or false
        //another example...
        System.out.println(arg1.getExtras().containsKey("message")); // true or false
        //if var exist only print or do some stuff
        if (arg1.hasExtra("message")) {
            //do what you want to
            System.out.println(arg1.getStringExtra("message"));
        }    
    }
}

public void checkStatusService(){
    if(MyService.serviceStatus!=null){
        if(MyService.serviceStatus == true){
            //do something
            //textview.text("Service is running");
        }else{
            //do something
            //textview.text("Service is not running");
        }
    }
}

Un service

public class MyService extends Service {

final static String SENDMESAGGE = "passMessage";

public static Boolean serviceStatus = false;

@Override
public void onCreate() {
    super.onCreate();
    serviceStatus=true;
}

@Nullable
@Override
    public IBinder onBind(Intent intent) {return null;}

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //you service etc...             
        passMessageToActivity("hello my friend this an example of send a string...");
        return START_STICKY;
    }

@Override
    public void onDestroy() {
        super.onDestroy();
        passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
        System.out.println("onDestroy");
        serviceStatus=false;
   }

private void passMessageToActivity(String message){
        Intent intent = new Intent();
        intent.setAction(SENDMESAGGE);
        intent.putExtra("message",message);
        sendBroadcast(intent);
    }
}
  • si nous ne désenregistrons pas BroadcastReceiver nous aurons une erreur, vous devez désenregistrer lorsque l'activité se poursuitPause, onStop, onDestroy ...
  • si vous n'enregistrez pas BroadcastReceiver lorsque vous revenez à l'activité, il n'écoutera rien du service ... le service enverra des informations à BroadcastReceiver mais il ne recevra rien car il n'est pas enregistré.
  • Lorsque vous créez plusieurs services, les services suivants vont commencer dans onStartCommand
  • Vous pouvez transmettre des informations au service avec intention et vous les obtenez dans onStartCommand
  • Différence sur return dans onStartCommand: Différence entre START_STICKY et START_REDELIVER_INTENT? et consultez le site officiel de google: Services
2
DarckBlezzer

Le moyen le plus simple et le plus efficace consiste à utiliser EventBus de GreenRobot.

Utilisez 3 étapes simples:

1 Définir les événements

public static class MessageEvent { /* Additional fields if needed */ }

2 Préparer les abonnés: Déclarez et annotez votre méthode d'abonnement, spécifiez éventuellement un mode de thread:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};

Enregistrez et désinscrivez votre abonné. Par exemple, sur Android, les activités et les fragments doivent normalement s'inscrire en fonction de leur cycle de vie:

@Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }

3 événements post:

EventBus.getDefault().post(new MessageEvent());
0
Khemraj

Le moyen très simple et puissant consiste à utiliser EventBus, vous pouvez l’ajouter à votre génération et profiter du modèle facile éditeur/abonné.

0
Melad
0
Dheeraj Sachan