Comunicación entre Actividad y Servicio

Estoy tratando de hacer mi propio MusicPlayer para Android. Cuando llegué a un problema es correr algunas cosas en segundo plano. La actividad principal maneja la GUI y hasta ahora todas las canciones están jugando. Quería separar GUI y clases de música. Quiero poner parte de la gestión de música en el servicio y dejar otras cosas como son ahora.

Mi problema es que no puedo organizar la comunicación entre la actividad y el servicio como mucha comunicación está sucediendo entre ellos, incluyendo objetos en movimiento en ambas direcciones. He intentado muchas técnicas que he buscado aquí en la pila de desbordamiento, pero cada vez que tenía problemas. Necesito Servicio para poder enviar objetos a la Actividad y viceversa. Cuando agrego widget también quiero que sea capaz de comunicarse con el servicio.

Cualquier sugerencia se aprecia, si necesita código fuente comentario lugar abajo, pero ahora en esta transición se convirtió en caótico.

¿Hay algún tutorial más avanzado sobre esto que llamar a un método que devuelve un número aleatorio del servicio? :PAG

EDIT: Posible solución es usar la biblioteca de RoboGuice y mover objetos con inyección

He implementado la comunicación entre la actividad y el servicio mediante la interfaz de enlace y devolución de llamada.

Para enviar datos al servicio utilicé Binder que recupera el servicio instace a la actividad, y luego la actividad puede acceder a métodos públicos en el servicio.

Para enviar datos de nuevo a la actividad del servicio, utilicé la interfaz de devolución de llamada como usted está utilizando cuando usted desea comunicarse entre el fragmento y la actividad.

Aquí hay algunos ejemplos de código para cada uno: El siguiente ejemplo muestra la relación bidireccional de actividad y servicio: La actividad tiene 2 botones: El primer botón iniciará y detendrá el servicio. El segundo botón iniciará un temporizador que se ejecutará en el servicio.

El servicio actualizará la actividad mediante la devolución de llamada con el progreso del temporizador.

Mi actividad:

//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)); } } 

Y aquí está el servicio:

  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); } } 

Actualización: 10 de julio de 2016

IMO Creo que usar BroadcastReceiver para eventos personalizados es la mejor manera como los mensajeros mencionados no manejan la recreación de la actividad en la rotación del dispositivo, así como posibles fugas de memoria.

Puede crear Custom BroadCast Receiver para eventos en la actividad, Entonces también puede usar Messengers.

  1. En su Activity

    Crear una clase MessageHandler como

     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; } } } 

    Ahora usted puede tener su instancia como

     public static Handler messageHandler = new MessageHandler(); 

    Inicie su Service con este objeto Handler como datos adicionales como

     Intent startService = new Intent(context, SERVICE.class) startService.putExtra("MESSENGER", new Messenger(messageHandler)); context.startService(startService); 
  2. En su Service recibe este objeto de la intención e inicializa la variable Messenger en Service as

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

    Y luego escribe un método sendMessage para enviar mensajes a la actividad.

     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(); } } 

El código de ejemplo anterior muestra y oculta un ProgressBar en Actividad a medida que se reciben mensajes de Servicio.

Los intentos son una buena solución para la comunicación entre Actividad y Servicio.

Una solución rápida para recibir intents en su servicio es la subclase de la clase IntentService . Maneja las solicitudes asincrónicas expresadas como Intents utilizando una cola y un subproceso de trabajo.

Para la comunicación desde el servicio a la actividad puede transmitir la intención, pero en lugar de usar sendBroadcast () normal desde el contexto, una manera más eficaz es utilizar LocalBroadcastManager de la biblioteca de soporte.

Ejemplo de servicio.

 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); } } 

Ejemplo de actividad

 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); } } 

Creo que hay un problema con la respuesta correcta. No tengo suficiente reputación para comentar sobre ello.

Derecho en la respuesta: Actividad llamada bindService () para obtener el puntero al servicio está bien. Dado que el contexto de servicio se mantiene cuando se mantiene la conexión.

Mal en la respuesta: el puntero de servicio a la clase de actividad para llamar de nuevo es mal camino. La instancia de actividad tal vez no nulo durante el contexto de la actividad es Release => excepción aquí.

Solución para el mal en la respuesta: servicio enviar intención a la actividad. Y la intención del receptor de la actividad vía BroadcastReceiver.

Nota: en este caso, servicio y actividad en el mismo proceso, debe utilizar LocalBroadcastManager para enviar la intención. Mejora el rendimiento y la seguridad

La mejor manera en este caso es comunicarse haciendo difusión de su servicio para diferentes acciones y recibiéndolo en su actividad. Puede crear una emisión personalizada y enviar algunos códigos que definen eventos específicos como completar, cambiar, preparar etc …

Este es un ejemplo simple de comunicación entre actividad y servicio

Actividad

 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"); } } } 

Servicio

 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 no cancelamos el registro de BroadcastReceiver, tendremos un error, debes anular el registro cuando la actividad vaya a onPause, onStop, onDestroy …
  • Si no registra BroadcastReceiver cuando vuelve a la actividad, no escuchará nada del servicio … el servicio enviará información a BroadcastReceiver pero no recibirá nada porque no está registrado.
  • Cuando se crea más de un servicio, los servicios siguientes van a comenzar en onStartCommand .
  • Usted puede pasar la información al servicio con la intención y usted lo consigue en onStartCommand
  • Diferencia acerca de la return en onStartCommand : ¿ Diferencia entre START_STICKY y START_REDELIVER_INTENT? Y consulte la página oficial de google: Servicios

Echa un vistazo a la documentación de android

http://developer.android.com/guide/components/bound-services.html#Binder

  • OnCreate de actividad de Android se llama dos veces cuando se navega de nuevo desde otra actividad
  • Android: pasa el camino del archivo entre dos Actividades
  • OnCreateView se llama varias veces / Trabajo con ActionBar y Fragmentos
  • Presionando el botón de inicio y acceder a la aplicación de nuevo desde la llamada del icono de la aplicación ¿Crear?
  • Cómo navegar desde una pantalla a otra pantalla
  • ¿Cómo obtener la instancia de actividad "llamando" de Actividad llamada?
  • ¿Cuáles son las diferencias entre la actividad y el fragmento
  • Implementación diferente para un botón de acción flotante en cada fragmento de una actividad
  • Android - launchMode = singleTask y Notificaciones?
  • Android: comprobando si la actividad se declara en Manifest en tiempo de ejecución
  • Distinguir entre presionar el botón "inicio" y abrir otra Actividad
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.