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

Resuelto Scott Saunders asked hace 54 años • 13 respuestas

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á algunos registros basados ​​en tiempo y GPS. Tendré una Actividad que se utilizará para iniciar y detener el Servicio.

Primero, necesito poder determinar 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 dudes 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 números enteros en este punto: principalmente mensajes de estado. Los mensajes no se enviarán con regularidad, 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 UI 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 escucha. 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 excesivo y no tengo idea de cómo la Actividad debería implementar writeToParcel()/readFromParcel().

¿Existe 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

Scott Saunders avatar Jan 01 '70 08:01 Scott Saunders
Aceptado

El autor de la pregunta probablemente ya 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 sencilla.

Añade un BroadcastReceivera tu actividad. Regístrelo para recibir alguna intención personalizada onResumey cancele su registro en onPause. Luego envíe esa intención desde su servicio cuando desee enviar sus actualizaciones de estado o lo que sea.

Asegúrate de no sentirte infeliz si alguna otra aplicación te escucha 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_INTENTes 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 como el mejor de la clase:

private DataUpdateReceiver dataUpdateReceiver;

Anulo onResumepara agregar esto:

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

Y lo anulo onPausepara agregar:

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

Ahora mi actividad está escuchando a mi servicio para decir "Oye, actualízate". Podría pasar datos en Intentlugar 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 la base de datos.

MaximumGoat avatar Jan 19 '2011 19:01 MaximumGoat

Hay tres formas obvias de comunicarse con los servicios:

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

En su caso, elegiría la opción 3. Haga una referencia estática al servicio en sí y complétela en onCreate():

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

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

Luego, al Activity.onCreate()iniciar el servicio, espere de forma asincrónica hasta que el servicio se inicie realmente (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 deberá llamar a 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 del contexto de su actividad, lo cual 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 tienes que utilizar la opción 1. o 2.

MrSnowflake avatar Mar 17 '2010 15:03 MrSnowflake

Úselo LocalBroadcastManagerpara registrar un receptor para escuchar una transmisión enviada desde el servicio local dentro de su aplicación, la referencia va aquí:

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

Jack Gao avatar Jan 13 '2012 02:01 Jack Gao

También puedes utilizarlo LiveDataque funciona como un archivo 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 en este blog.

zeenosaur avatar Nov 19 '2018 07:11 zeenosaur

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

Puede encontrar un ejemplo completo en las demostraciones de API: MessengerService y MessengerServiceActivity . Consulte el ejemplo completo de cómo funciona MyService.

Kjetil avatar Feb 25 '2012 21:02 Kjetil