Cómo hacer que el servicio Android se comunique con la actividad

Estoy escribiendo mi primera aplicación de Android y tratando de obtener mi cabeza en torno a la comunicación entre los servicios y las actividades. Tengo un servicio que se ejecutará en segundo plano y hacer algunos gps y tiempo basado en el registro. Tendré una Actividad que se utilizará para iniciar y detener el Servicio.

Así que primero, tengo que ser capaz de averiguar si el servicio se está ejecutando cuando se inicia la actividad. Hay algunas otras preguntas aquí acerca de eso, así que creo que puedo entender eso (pero no dude en ofrecer consejo).

Mi verdadero problema: si la Actividad se está ejecutando y el Servicio se inicia, necesito una forma para que el Servicio envíe mensajes a la Actividad. Cuerdas simples y números enteros en este punto – mensajes de estado en su mayoría. Los mensajes no suceden con regularidad, así que no creo que el servicio de sondeo sea un buen camino si hay otra forma. Sólo quiero esta comunicación cuando la Actividad ha sido iniciada por el usuario – No quiero iniciar la Actividad del Servicio. En otras palabras, si inicia la actividad y el servicio se está ejecutando, verá algunos mensajes de estado en la interfaz de usuario de actividad cuando sucede algo interesante. Si no inicia la Actividad, no verá estos mensajes (no son tan interesantes).

Parece que debería ser capaz de determinar si el servicio se está ejecutando, y si es así, agregar la actividad como un oyente. A continuación, elimine la actividad como un oyente cuando la actividad se detiene o se detiene. ¿Es realmente posible? La única manera que puedo imaginar para hacerlo es tener el implemento de la actividad parcelable y construir un archivo de AIDL así que puedo pasarlo a través de la interfaz alejada del servicio. Eso parece como exceso, sin embargo, y no tengo ni idea de cómo la actividad debe implementar writeToParcel () / readFromParcel ().

¿Hay una manera más fácil o mejor? Gracias por cualquier ayuda.

EDITAR:

Para cualquiera que esté interesado en esto más adelante, hay código de ejemplo de Google para manejar esto a través de AIDL en el directorio de samples: /apis/app/RemoteService.java

Hay tres maneras obvias de comunicarse con los servicios:

  1. Usando Intenciones
  2. Uso de AIDL
  3. Utilizar el objeto de servicio en sí (como singleton)

En su caso, me gustaría ir con la opción 3. Hacer una referencia estática para el servicio de sí mismo y llenarlo en onCreate ():

void onCreate(Intent i) { sInstance = this; } 

Hacer una función estática MyService getInstance() , que devuelve la sInstance estática.

A continuación, en Activity.onCreate() inicia el servicio, de forma asíncrona espere hasta que el servicio se inicie realmente (puede hacer que su servicio notifique su aplicación está lista enviando una intención a la actividad) y obtenga su instancia. Cuando tenga la instancia, registre su objeto de escucha de servicio a su servicio y está configurado. NOTA: al editar vistas dentro de la actividad, debe modificarlas en el subproceso de interfaz de usuario, es probable que el servicio ejecute su propio subproceso, por lo que debe llamar a Activity.runOnUiThread() .

Lo último que debe hacer es quitar la referencia a su objeto de escucha en Activity.onPause() , de lo contrario una instancia de su contexto de actividad se verá, no es buena.

NOTA: Este método sólo es útil cuando su aplicación / Actividad / tarea es el único proceso que tendrá acceso a su servicio. Si éste no es el caso usted tiene que utilizar la opción 1 o 2.

El asker probablemente desde hace mucho tiempo pasó esto, pero en caso de que alguien más busque esto …

Hay otra manera de manejar esto, que creo que podría ser la más simple.

Agregue un BroadcastReceiver a su actividad. Regístrelo para recibir algún intento personalizado en onResume y anótelo en onPause. A continuación, enviar esa intención de su servicio cuando desea enviar sus actualizaciones de estado o lo que tiene.

Asegúrese de que no sería infeliz si alguna otra aplicación escuchada por su intención (¿alguien podría hacer algo malicioso?), Pero más allá de eso, usted debe estar bien.

Se solicitó una muestra de código:

En mi servicio, tengo:

 // Do stuff that alters the content of my local SQLite Database sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT)); 

(RefreshTask.REFRESH_DATA_INTENT es sólo una cadena constante.)

En mi actividad de escucha, defino mi 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 } } } 

Declaro a mi receptor en la parte superior de la clase:

 private DataUpdateReceiver dataUpdateReceiver; 

Anular onResume para agregar:

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

Y anularé onPause para agregar:

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

Ahora mi actividad está escuchando mi servicio para decir "Hey, vaya a ponerse al día". Podría pasar los datos en el intento en lugar de actualizar las tablas de la base de datos y luego volver a buscar los cambios dentro de mi actividad, pero como quiero que los cambios persistan de todos modos, tiene sentido pasar los datos a través de db.

Utilice LocalBroadcastManager para registrar un receptor para escuchar una emisión enviada desde localservice dentro de su aplicación, la referencia va aquí:

http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

El uso de un Messenger es otra forma sencilla de comunicarse entre un Servicio y una Actividad.

En la actividad, cree un controlador con un Messenger correspondiente. Esto manejará los mensajes de su Servicio.

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

El Messenger se puede pasar al servicio adjuntándolo a un mensaje:

 Message message = Message.obtain(null, MyService.ADD_RESPONSE_HANDLER); message.replyTo = messenger; try { myService.send(message); catch (RemoteException e) { e.printStackTrace(); } 

Un ejemplo completo se puede encontrar en las demos API: MessengerService y MessengerServiceActivity . Consulte el ejemplo completo de cómo funciona MyService.

Me sorprende que nadie haya dado referencia a Otto evento Bus biblioteca

http://square.github.io/otto/

He estado usando esto en mis aplicaciones Android y funciona sin problemas.

El otro método que no se menciona en los otros comentarios es vincular al servicio de la actividad que utiliza bindService () y obtener una instancia del servicio en la devolución de llamada ServiceConnection. Como se describe aquí http://developer.android.com/guide/components/bound-services.html

Otra manera podría ser el uso de observadores con una clase de modelo falso a través de la actividad y el servicio en sí, la aplicación de una variación de patrón MVC. No sé si es la mejor manera de lograr esto, pero es la forma en que funcionó para mí. Si usted necesita algún ejemplo pida él y fijaré algo.

Para hacer un seguimiento de @MrSnowflake respuesta con un ejemplo de código. Esta es la clase de Application código abierto XABBER ahora . La clase Application está centralizando y coordinando Listeners y ManagerInterfaces y más. Los gestores de todo tipo se cargan dinámicamente. Activity´s iniciada en el Xabber informará en qué tipo de Listener son. Y cuando un Service inicia informe en la clase de la Application como comenzada. Ahora para enviar un mensaje a una Activity todo lo que tienes que hacer es hacer que tu Activity convierta en un listener de qué tipo necesitas. En OnStart() OnPause() register / unreg. El Service puede solicitar a la clase de la Application sólo para ese listener que necesita hablar y si está allí entonces la Actividad está lista para recibir.

Pasando por la clase de Application verá que hay un botín más en marcha, entonces esto.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.