studio programacion móviles libro fragments entre desarrollo curso comunicacion aplicaciones android android-activity android-service message-passing

móviles - manual de programacion android pdf



Comunicación entre actividad y servicio (9)

Estoy tratando de hacer mi propio MusicPlayer para Android. Donde llegué a un problema está ejecutando algunas cosas en segundo plano. La actividad principal administra la GUI y hasta ahora todas las canciones se están reproduciendo. Quería separar la GUI y las clases de reproducción de música. Quiero poner la administración de música en el servicio y dejar otras cosas como están ahora.

Mi problema es que no puedo organizar la comunicación entre la actividad y el servicio, ya que hay mucha comunicación entre ellos, incluido el movimiento de objetos en ambas direcciones. Probé muchas técnicas que busqué aquí en Stack Overflow pero cada vez que tuve problemas. Necesito el Servicio para poder enviar objetos a la Actividad y viceversa. Cuando agrego widget también quiero que se pueda comunicar con el Servicio.

Se agradecen todos los consejos, si necesita el código fuente, coloque el comentario a continuación, pero ahora en esta transición se volvió 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

EDITAR: La solución posible es usar la biblioteca RoboGuice y mover objetos con inyección


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

Justo en la respuesta: La actividad llamada bindService () para obtener el puntero al Servicio está bien. Porque el contexto del servicio se mantiene cuando se mantiene la conexión.

mal en la respuesta: puntero de servicio a clase de actividad para devolver la llamada es mala. La instancia de la actividad puede no ser nula durante el contexto de la actividad. Se trata de una excepción => excepción aquí.

solución para lo incorrecto en la respuesta: servicio enviar intento a Actividad. y la intención del receptor de actividad a través de BroadcastReceiver.

Nota: en este caso, Servicio y Actividad en el mismo Proceso, debe usar LocalBroadcastManager para enviar intenciones. Mejora el rendimiento y la seguridad


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, debe anular el registro cuando la actividad se active 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 crea más de un servicio, los siguientes servicios comenzarán en onStartCommand .
  • Puede pasar información al servicio con intención y la puede obtener en onStartCommand
  • Diferencia sobre el return en onStartCommand : diferencia entre START_STICKY y START_REDELIVER_INTENT? y consulte el sitio web oficial de google: Services

Implementé la comunicación entre Actividad y Servicio utilizando la interfaz de Vinculación y Devolución de llamada.

Para enviar datos al servicio, utilicé Binder, que devuelve el Servicio a la Actividad, y luego la Actividad puede acceder a los métodos públicos en el Servicio.

Para enviar datos a la Actividad del Servicio, utilicé la interfaz de Devolución de llamada como la que está utilizando cuando desea comunicarse entre Fragmento y Actividad.

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

El servicio actualizará la Actividad a través de una 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); } }


La manera más fácil y eficiente será usar EventBus de EventBus .

Use 3 pasos simples:

1 Define eventos

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

2 Prepare suscriptores: declare y anote su método de suscripción; opcionalmente, especifique un modo de secuencia:

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

Regístrese y elimine el registro de su suscriptor. Por ejemplo, en Android, las actividades y los fragmentos generalmente deberían registrarse de acuerdo con su ciclo de vida:

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

3 eventos de publicaciones:

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


La mejor manera en este caso es comunicarse haciendo transmisiones desde su servicio para diferentes acciones y recibirlas en su actividad. Puede crear una transmisión personalizada y enviar algunos códigos que definen eventos específicos como completar, cambiar, preparar, etc.


Los intentos son una buena solución para la comunicación entre la actividad y el servicio.

Una solución rápida para los intentos de recepción en su servicio es la subclasificación de la clase IntentService . Maneja solicitudes asíncronas expresadas como Intenciones utilizando una cola y un hilo de trabajo.

Para la comunicación del servicio a la actividad, puede transmitir el intento, pero en lugar de usar sendBroadcast normal () desde Context, una forma más eficiente es usar LocalBroadcastManager desde 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); } }

Actividad de ejemplo

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


Una manera muy fácil pero poderosa es usar EventBus , puede agregarlo a su compilación de gradle y disfrutar del sencillo patrón de editor / suscriptor.



Actualización: 10 de julio de 2016

IMO Creo que usar BroadcastReceiver para eventos personalizados es una mejor manera que la mencionada por los Mensajeros de no manejar la actividad de recreación en la rotación del dispositivo, así como posibles fugas de memoria.

Puede crear un receptor BroadCast personalizado para eventos en la actividad. Luego, también puede usar Mensajeros.

  1. En tu 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 puedes tener su instancia como

    public static Handler messageHandler = new MessageHandler();

    Comience su Service con este objeto controlador 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 del intento e inicializa la variable Messenger en el Servicio como

    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 una barra de progreso en la actividad a medida que se reciben los mensajes del servicio.