
scott saunders
Estoy escribiendo mi primera aplicación para Android y tratando de entender la comunicación entre servicios y actividades. Tengo un servicio que se ejecutará en segundo plano y realizará un registro basado en gps y tiempo. Tendré una Actividad que se utilizará para iniciar y detener el Servicio.
Entonces, primero, necesito poder averiguar si el Servicio se está ejecutando cuando se inicia la Actividad. Hay algunas otras preguntas aquí sobre eso, así que creo que puedo resolverlo (pero no dude en ofrecer consejos).
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. Cadenas simples y enteros en este punto: mensajes de estado en su mayoría. Los mensajes no aparecerán regularmente, por lo que no creo que sondear el servicio sea una buena manera de hacerlo si hay otra manera. Solo quiero esta comunicación cuando el usuario haya iniciado la Actividad; no quiero iniciar la Actividad desde el 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 la actividad cuando suceda algo interesante. Si no inicias la Actividad, no verás estos mensajes (no son tan interesantes).
Parece que debería poder determinar si el Servicio se está ejecutando y, de ser así, agregar la Actividad como oyente. Luego, elimine la actividad como oyente cuando la actividad se detenga o se detenga. ¿Es eso realmente posible? La única forma en que puedo hacerlo es hacer que la Actividad implemente Parcelable y cree un archivo AIDL para poder pasarlo a través de la interfaz remota del Servicio. Sin embargo, eso parece una exageración, y no tengo idea de cómo la Actividad debería 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 un código de muestra de Google para manejar esto a través de AIDL en el directorio de muestras: /apis/app/RemoteService.java

Máximo Cabra
El autor de la pregunta probablemente haya superado esto hace mucho tiempo, pero en caso de que alguien más busque esto…
Hay otra forma de manejar esto, que creo que podría ser la más simple.
Agrega un BroadcastReceiver
a tu actividad. Regístrelo para recibir alguna intención personalizada en onResume
y darlo de baja en onPause
. Luego envíe esa intención desde su servicio cuando desee enviar sus actualizaciones de estado o lo que sea.
Asegúrese de que no se sentiría infeliz si alguna otra aplicación escuchara su Intent
(¿alguien podría hacer algo malicioso?), pero más allá de eso, deberías estar bien.
Se solicitó una muestra de código:
En mi servicio, tengo esto:
// Do stuff that alters the content of my local SQLite Database
sendBroadcast(new Intent(RefreshTask.REFRESH_DATA_INTENT));
(RefreshTask.REFRESH_DATA_INTENT
es solo 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;
anulo onResume
para agregar esto:
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
y anulo onPause
para agregar:
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
Ahora mi actividad está escuchando mi servicio para decir “Oye, ve a actualizarte”. Podría pasar datos en el Intent
en lugar de actualizar las tablas de la base de datos y luego regresar para encontrar 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.

señorcopo de nieve
Hay tres formas obvias de comunicarse con los servicios:
- Uso de intenciones
- Usando AIDL
- Usando el propio objeto de servicio (como singleton)
En su caso, elegiría la opción 3. Haga una referencia estática al propio servicio y rellénelo en onCreate():
void onCreate(Intent i) {
sInstance = this;
}
Hacer una función estática MyService getInstance()
que devuelve la estática sInstance
.
luego en Activity.onCreate()
inicia el servicio, espera de forma asíncrona hasta que el servicio realmente se inicie (puede hacer que su servicio notifique a su aplicación que está lista enviando una intención a la actividad) y obtenga su instancia. Cuando tenga la instancia, registre su objeto de escucha de servicio en su servicio y estará listo. NOTA: al editar Vistas dentro de la Actividad, debe modificarlas en el subproceso de la interfaz de usuario, el servicio probablemente ejecutará su propio Subproceso, por lo que debe llamar Activity.runOnUiThread()
.
Lo último que debe hacer es eliminar la referencia a su objeto de escucha en Activity.onPause()
de lo contrario, se filtrará una instancia de su contexto de actividad, no es bueno.
NOTA: este método solo es útil cuando su aplicación/actividad/tarea es el único proceso que accederá a su servicio. Si este no es el caso, debe usar la opción 1. o 2.

Jack Gao
Utilizar LocalBroadcastManager
para registrar un receptor para escuchar una transmisión enviada desde un servicio local dentro de su aplicación, la referencia va aquí:
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Me sorprende que nadie haya dado referencia a la biblioteca Otto event Bus
http://square.github.io/otto/
He estado usando esto en mis aplicaciones de Android y funciona a la perfección.

Kjetil
El uso de Messenger es otra forma sencilla de comunicarse entre un Servicio y una Actividad.
En la Actividad, cree un Controlador con un Mensajero 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();
}
Puede encontrar un ejemplo completo en las demostraciones de la API: MessengerService y MessengerServiceActivity. Consulte el ejemplo completo para saber cómo funciona MyService.

Vlad
También puede usar LiveData
que funciona como un EventBus
.
class MyService : LifecycleService() {
companion object {
val BUS = MutableLiveData<Any>()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val testItem : Object
// expose your data
if (BUS.hasActiveObservers()) {
BUS.postValue(testItem)
}
return START_NOT_STICKY
}
}
Luego agregue un observador de su Activity
.
MyService.BUS.observe(this, Observer {
it?.let {
// Do what you need to do here
}
})
Puedes leer más de esto Blog.

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