android - intent - ¿Forma más eficiente de actualizar la interfaz de usuario del servicio que intenciones?
service android ejemplo (2)
Actualmente tengo un servicio en Android que es un cliente de muestra VOIP, por lo que escucha los mensajes SIP y, si recibe uno, inicia una pantalla de actividad con los componentes de la interfaz de usuario.
Luego, los siguientes mensajes SIP determinan qué actividad se mostrará en la pantalla. Por ejemplo, si es una llamada entrante, mostrará Contestar o Rechazar o una llamada saliente que mostrará una pantalla de marcación.
En el minuto, utilizo Intents para que la Actividad sepa qué estado debe mostrar.
Un ejemplo es el siguiente:
Intent i = new Intent();
i.setAction(SIPEngine.SIP_TRYING_INTENT);
i.putExtra("com.net.INCOMING", true);
sendBroadcast(i);
Intent x = new Intent();
x.setAction(CallManager.SIP_INCOMING_CALL_INTENT);
sendBroadcast(x);
Log.d("INTENT SENT", "INTENT SENT INCOMING CALL AFTER PROCESSINVITE");
Por lo tanto, la actividad tendrá un receptor de difusión registrado para estos intentos y cambiará su estado de acuerdo con el último intento que recibió.
Código de muestra de la siguiente manera:
SipCallListener = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(SIPEngine.SIP_RINGING_INTENT.equals(action)){
Log.d("cda ", "Got RINGING action SIPENGINE");
ringingSetup();
}
if(CallManager.SIP_INCOMING_CALL_INTENT.equals(action)){
Log.d("cda ", "Got PHONE RINGING action");
incomingCallSetup();
}
}
};
IntentFilter filter = new IntentFilter(CallManager.SIP_INCOMING_CALL_INTENT);
filter.addAction(CallManager.SIP_RINGING_CALL_INTENT);
registerReceiver(SipCallListener, filter);
Esto funciona, sin embargo, parece que no es muy eficiente, las Intenciones se transmitirán por todo el sistema y las Intenciones tienen que dispararse para diferentes estados parece que podría volverse ineficiente cuanto más tengo que incluir como agregar complejidad.
Entonces, me preguntaba si existe una manera diferente más eficiente y más limpia de hacer esto.
¿Hay alguna manera de mantener la transmisión de Intents solo dentro de una aplicación?
¿Las devoluciones de llamada serían una mejor idea? Si es así, ¿por qué y de qué manera deberían implementarse?
Puede enviar intenciones de transmisión solo a su propia aplicación y no a todo el sistema con LocalBroadcastManager :
Ayudante para registrarse y enviar transmisiones de Intenciones a objetos locales dentro de su proceso. Esto tiene una serie de ventajas sobre el envío de transmisiones globales con sendBroadcast (Intento):
Sabes que los datos que estás transmitiendo no saldrán de tu aplicación, por lo que no debes preocuparte por filtrar información privada.
No es posible que otras aplicaciones envíen estas transmisiones a su aplicación, por lo que no debe preocuparse por tener agujeros de seguridad que puedan explotar.
Es más eficiente que enviar una transmisión global a través del sistema.
Sin embargo, aún recomendaría ir con el enfoque de Servicio y vincularlo localmente y hablar a través de Handler cuando sea necesario para actualizar los componentes de la interfaz de usuario para mayor eficiencia.
ACTUALIZACIÓN 2015:
Esta pregunta / respuesta todavía tiene un poco de actividad, pero tiene más de 5 años y las cosas han cambiado bastante. Hace 5 años, la respuesta a continuación fue cómo lo habría manejado. Más tarde escribí una solución de inyección de dependencia muy ligera que estaba usando por un tiempo (que mencioné en los comentarios). Hoy en día, respondería esta pregunta usando Dagger y RxAndroid. Dagger para inyectar una clase "mediador" tanto en el Servicio como en todas las Actividades que necesitan ser notificadas, el Servicio empujaría la actualización de estado a la clase mediadora, y la clase mediadora expondría un observable para las actividades para consumir la actualización de estado ( en lugar del receptor de difusión del OP).
Respuesta original
Normalmente hago una subclase de aplicación y dejo que mi comunicación dentro de la aplicación pase por esta clase (o hago que un mediador propiedad de la aplicación haga el trabajo ... independientemente, la aplicación es el punto de entrada para que el servicio se comunique). Tengo un servicio encuadernado que también necesita actualizar la UI (mucho más simple que la tuya, pero la misma idea) y básicamente le dice a la aplicación su nuevo estado y la aplicación puede pasar esta información de una forma u otra a la actual actividad activa. También puede mantener un puntero a la actividad actualmente activa (si hay más de uno) y tomar decisiones sobre si simplemente actualizar la actividad actual, transmitir la intención de iniciar una actividad diferente, ignorar el mensaje, etc. Lo haría también la actividad de subclase y su nueva clase base de actividad le indica a la aplicación que actualmente es la activa en onResume y que está pausada en onPause (para los casos en que su servicio se ejecuta en segundo plano y todas las actividades están pausadas).
EDITAR:
En respuesta al comentario, aquí hay más detalles.
En la actualidad, su aplicación consiste en clases derivadas de la actividad y derivadas del servicio. Intrínsecamente, obtienes la funcionalidad de una instancia de la clase android.app.Application. Esto se declara en su manifiesto (por defecto) con la siguiente línea:
<application android:icon="@drawable/icon" android:label="@string/app_name">
El elemento de la aplicación en su manifiesto no usa el atributo android: name, por lo que solo crea una instancia de la clase android.app.Application predeterminada para representar el contexto global de la aplicación.
En mis aplicaciones, creo una subclase de Aplicación (ApplicationEx, por ejemplo) y le digo a mi aplicación a través del manifiesto que esta es la clase para instanciar como MI contexto global de la aplicación. Por ejemplo:
<application
android:name="com.mycompany.myapp.app.ApplicationEx"
android:icon="@drawable/app_icon"
android:label="@string/app_name">
Ahora puedo agregar métodos a ApplicationEx para actividades y servicios para usar para comunicarme. Siempre hay una sola instancia de su contexto de aplicación global, por lo que este es su punto de partida si algo tiene que ser global para su aplicación.
Una segunda parte de esto es que en lugar de derivar mis servicios y actividades de Servicio y Actividad, creo una subclase de cada uno con un método getAppContext que arroja el valor de retorno de getApplicationContext (que ya existe en ambas clases porque derivan de Contexto ) a mi clase ApplicationEx.
Asi que........
Dicho todo esto, agrega una propiedad CurrentActivity a su clase ApplicationEx de tipo Activity (o ActivityBase si la subclase como yo). En el método onResume de ActivityBase, te pasas a ApplicationEx para que establezca CurrentActivity a esa actividad. Ahora, puede exponer métodos en ApplicationEx para pasar información directamente a la actividad actual en lugar de confiar en los mecanismos de intención.
Eso es tan claro como puedo hacerlo