sobre - Cómo hacer que el servicio de Android se comunique con la actividad.
libros sobre youtube pdf (11)
Además de LocalBroadcastManager, Event Bus y Messenger ya contestados en esta pregunta, podemos usar la intención pendiente para comunicarnos desde el servicio.
Como se menciona here en mi blog
La comunicación entre el servicio y la Actividad se puede hacer usando PendingIntent. Para que podamos usar createPendingResult () .createPendingResult () crea un nuevo objeto PendingIntent que puede entregar al servicio para usar y enviar datos de resultados a su actividad en onActivityResult (int, int, Intent) devolución de llamada. Dado que un PendingIntent es Parcelable y, por lo tanto, se puede poner en un Intent extra, su actividad puede pasar este PendingIntent al servicio. El servicio, a su vez, puede llamar al método send () en el PendingIntent para notificar al Actividad a través de onActivityResult de un evento.
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); } } } } }
Estoy escribiendo mi primera aplicación de Android y tratando de entender la comunicación entre servicios y actividades. Tengo un servicio que se ejecutará en segundo plano y hará algunos gps y registro de 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 siéntase libre de ofrecer consejos).
Mi problema real: 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 se realizarán con regularidad, por lo que no creo que encuestar el servicio sea una buena manera de hacerlo si hay otra. Solo quiero esta comunicación cuando el Usuario ha 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 inicia la Actividad, no verá 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 de entenderlo es hacer que Activity implemente Parcelable y compile un archivo AIDL para que pueda 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 debe implementar writeToParcel () / readFromParcel ().
¿Hay alguna forma más fácil o mejor? Gracias por cualquier ayuda.
EDITAR:
Para cualquier persona que esté interesada 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
Como lo mencionó Madhur, puede usar un bus para comunicarse.
En caso de usar un Bus tienes algunas opciones:
Biblioteca de autobuses de eventos de Otto (en desuso a favor de RxJava)
EventBus del robot verde
http://greenrobot.org/eventbus/
NYBus (RxBus, implementado usando RxJava. Muy similar al EventBus)
El otro método que no se menciona en los otros comentarios es vincularse al servicio de la actividad mediante 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
Hay tres formas obvias de comunicarse con los servicios:
- Usando intenciones
- Usando AIDL
- 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 y rellénelo en onCreate ():
void onCreate(Intent i) {
sInstance = this;
}
Haga una función estática MyService getInstance()
, que devuelve la sInstance
estática.
Luego, en Activity.onCreate()
inicia el servicio, espere de forma asíncrona hasta que el servicio se inicie realmente (puede hacer que su servicio le notifique a su aplicación que está lista enviando un intento 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 a Activity.runOnUiThread()
.
Lo último que debe hacer es eliminar la referencia a su objeto de escucha en Activity.onPause()
, de lo contrario se perderá 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, tiene que usar la opción 1 o 2.
Me sorprende que nadie haya hecho referencia a la biblioteca de autobuses de eventos de Otto.
He estado usando esto en mis aplicaciones de Android y funciona perfectamente.
Otra forma podría ser usar observadores con una clase de modelo falso a través de la actividad y el servicio en sí mismo, implementando 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 necesitas algún ejemplo, pídelo y publicaré algo.
Para dar seguimiento a la respuesta de @MrSnowflake con un ejemplo de código. Esta es la clase de Application
código abierto ahora XABBER . La clase de Application
está centralizando y coordinando escuchas y interfaces de administrador y más. Los administradores de todo tipo se cargan dinámicamente. Activity´s
iniciadas en Xabber informarán de qué tipo de Listener
son. Y cuando un Service
inicia, informe a la clase de Application
como se inició. Ahora, para enviar un mensaje a una Activity
todo lo que tiene que hacer es hacer que su Activity
convierta en un listener
de qué tipo necesita. En OnStart()
OnPause()
register / unreg. El Service
puede solicitar a la clase de Application
solo para el listener
que necesita hablar y si está allí, la Actividad está lista para recibir.
Al pasar por la clase de Application
, verás que hay más botín que esto.
Probablemente, el autor de la pregunta haya pasado de largo esto, 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.
Agregue un BroadcastReceiver a su actividad. Regístrelo para recibir un intento personalizado en onResume y anule el registro en onPause. Luego, envíe ese intento desde su servicio cuando desee enviar sus actualizaciones de estado o lo que sea.
Asegúrate de no ser infeliz si alguna otra aplicación escuchara tu Intención (¿podría alguien hacer algo malicioso?), Pero más allá de eso, deberías estar bien.
Se solicitó código de muestra:
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 mi receptor en la parte superior de la clase:
private DataUpdateReceiver dataUpdateReceiver;
Anulo onResume para agregar:
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 es escuchar mi servicio para decir "Oye, actualízate". Podría pasar datos en Intent en lugar de actualizar las tablas de la base de datos y luego volver 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.
También puede usar LiveData
que funciona como un EventBus
.
class MyService : LifecycleService() { companion object { var BUS = MutableLiveData<Object>() } 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 agrega un observador de tu Activity
.
MyService.BUS.observe(this, Observer { it?.let { // Do what you need to do here } })
Puedes leer más de este blog.
Usar un Messenger es otra forma sencilla de comunicarse entre un Servicio y una Actividad.
En la Actividad, crea un Handler 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();
}
Se puede encontrar un ejemplo completo en las demostraciones API: MessengerService y MessengerServiceActivity . Consulte el ejemplo completo de cómo funciona MyService.
Use LocalBroadcastManager
para registrar un receptor para escuchar una transmisión enviada desde localservice dentro de su aplicación, la referencia va aquí:
developer.android.com/reference/android/support/v4/content/…