Cómo hacer que el Servicio Android se comunique con la actividad
Estoy escribiendo mi primera aplicación para Android y tratando de conseguir mi cabeza alrededor de la comunicación entre servicios y actividades. Tengo un Servicio que se ejecutará en segundo plano y hacer un poco de gps y registro basado en el tiempo. Tendré una Actividad que se utilizará para iniciar y detener el Servicio.
Así que primero, necesito ser capaz de averiguar si el Servicio se está ejecutando cuando se inicia la Actividad. Hay algunas otras preguntas aquí sobre eso, así que creo que puedo averiguarlo (pero siéntete libre para ofrecer asesoramiento).
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 sucederán regularmente, así que no creo que encuestar al servicio sea una buena manera de hacerlo si hay otra manera. Solo quiero esta comunicación cuando la Actividad ha sido iniciada por el usuario-no quiero iniciar la Actividad desde el Servicio. En otros 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 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 oyente cuando la Actividad se detenga o se detenga. ¿Es realmente posible? La única manera que puedo imaginar para hacerlo es hacer que la Actividad implemente Parcelable y construya un archivo AIDL para que pueda pasarlo a través de la interfaz remota del Servicio. Sin embargo, eso parece exagerado, y no tengo idea de cómo debería implementar la Actividad 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 ejemplo de Google para manejar esto a través de AIDL en el directorio de muestras: / apis / app / RemoteService.java
10 answers
Hay tres maneras obvias de comunicarse con los servicios:
- Usando Intents
- Usando AIDL
- Usando el propio objeto de servicio (como singleton)
En su caso, yo iría con la opción 3. Haga una referencia estática al servicio it self 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, espere asíncronamente hasta que el servicio se inicie realmente (puedes hacer que tu servicio notifique a tu app que está lista enviando una intent a la actividad.) y obtener su instancia. Cuando tenga la instancia, registre su objeto de escucha de servicio en su servicio y estará configurado. NOTA: cuando edites Vistas dentro de la Actividad deberías modificarlas en el subproceso de IU, el servicio probablemente ejecutará su propio Subproceso, por lo que necesitas llamar a Activity.runOnUiThread()
.
Lo último que debe hacer es eliminar la referencia a su objeto de escucha en Activity.onPause()
, de lo contrario una instancia de su contexto de actividad se filtrará, 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-29 08:30:05
El preguntador probablemente ha pasado de 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.
Agrega un BroadcastReceiver a tu Actividad. Registrarse para recibir personalizado de intención en onResume y anular el registro de onPause. Luego envía esa intent desde tu servicio cuando quieras enviar tus actualizaciones de estado o lo que tengas.
Asegúrese de que no sería infeliz si alguna otra aplicación escuchó su intención (¿podría alguien hacer algo malicioso?), pero más allá de eso, deberías 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 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 añadir:
if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(RefreshTask.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
Y sobreescribo onPause para agregar:
if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
Ahora mi actividad es escuchar a mi servicio para decir " Oye, ve a actualizarte."Podría pasar datos en la Intent en lugar de actualizar las tablas de la base de datos y luego volver a 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 la base de datos.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 05:02:32
Use LocalBroadcastManager
para registrar un receptor para escuchar una transmisión enviada desde localservice dentro de su aplicación, la referencia va aquí:
Http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-27 11:41:11
Me sorprende que nadie haya dado referencia a Otto event Bus library
He estado usando esto en mis aplicaciones de Android y funciona a la perfección.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-11 11:22:14
Usar un 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 Mensajero 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 demostraciones de la API: MessengerService y MessengerServiceActivity. Consulte el ejemplo completo para ver cómo funciona MyService.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 22:37:28
El otro método que no se menciona en los otros comentarios es enlazar al servicio desde la actividad usando 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-04 20:35:20
Otra forma podría ser usar observadores con una clase de modelo falsa a través de la actividad y el propio servicio, implementando una variación de patrón MVC. No se si es la mejor manera de lograr esto, pero es la manera que funcionó para mí. Si necesitas algún ejemplo pídelo y publicaré algo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-26 13:58:03
Para seguir la respuesta de @MrSnowflake con un ejemplo de código.
Esta es la clase XABBER now open source Application
. La clase Application
está centralizando y coordinando Listeners
y ManagerInterfaces y más. Los gestores de todo tipo se cargan dinámicamente. Activity´s
iniciado en el Xabber informará en qué tipo de Listener
son. Y cuando un Service
iniciarlo informe a la clase Application
como iniciado. Ahora para enviar un mensaje a un Activity
todo lo que tienes que hacer es hacer que tu Activity
se convierta en un listener
de qué tipo lo necesitas. En el OnStart()
OnPause()
registrarse / unreg. El Service
puede pedir a la clase Application
solo que listener
necesita hablar y si está allí, entonces la Actividad está lista para recibir.
Pasando por la clase Application
verás que hay un botín más que esto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 10:46:45
Como menciona Madhur, puede usar un bus para la comunicación.
En caso de usar un Bus tienes algunas opciones:
Biblioteca Otto event Bus (en desuso a favor de RxJava)
EventBus del robot verde
Http://greenrobot.org/eventbus /
NYBus (RxBus, implementado usando RxJava. muy similar al EventBus)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-07 13:41:57
Además de LocalBroadcastManager , Event Bus y Messenger ya contestados en esta pregunta,podemos usar Pending Intent para comunicarnos desde el servicio.
Como se mencionó aquí en mi entrada de blog
La comunicación entre el servicio y la actividad se puede hacer utilizando PendingIntent.Para eso podemos usar createPendingResult ().createPendingResult () crea un nuevo PendingIntent objeto que puede entregar al servicio para usar y enviar resultado datos de vuelta a su actividad dentro de onActivityResult (int, int, Intención) callback.Puesto que un PendingIntent es Parcelable, y puede por lo tanto se pone en una intención extra, su actividad puede pasar esto PendingIntent al servicio.El servicio, a su vez, puede llamar enviar() método sobre el PendingIntent para notificar la actividad a través de Onactivity Result of an event.
Actividad
public class PendingIntentActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PendingIntent pendingResult = createPendingResult( 100, new Intent(), 0); Intent intent = new Intent(getApplicationContext(), PendingIntentService.class); intent.putExtra("pendingIntent", pendingResult); startService(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 100 && resultCode==200) { Toast.makeText(this,data.getStringExtra("name"),Toast.LENGTH_LONG).show(); } super.onActivityResult(requestCode, resultCode, data); } }
Servicio
public class PendingIntentService extends Service { private static final String[] items= { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", "adipiscing", "elit", "morbi", "vel", "ligula", "vitae", "arcu", "aliquet", "mollis", "etiam", "vel", "erat", "placerat", "ante", "porttitor", "sodales", "pellentesque", "augue", "purus" }; private PendingIntent data; @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { data = intent.getParcelableExtra("pendingIntent"); new LoadWordsThread().start(); return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); } class LoadWordsThread extends Thread { @Override public void run() { for (String item : items) { if (!isInterrupted()) { Intent result = new Intent(); result.putExtra("name", item); try { data.send(PendingIntentService.this,200,result); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } SystemClock.sleep(400); } } } } }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-27 09:08:45