Android equivalente a NSNotificationCenter

95

En el proceso de transferir una aplicación de iPhone a Android, estoy buscando la mejor manera de comunicarme dentro de la aplicación. Las intenciones parecen ser el camino a seguir, ¿es esta la mejor (única) opción? NSUserDefaults parece mucho más liviano que los Intents tanto en rendimiento como en codificación.

También debo agregar que tengo una subclase de aplicación para el estado, pero necesito hacer que otra actividad sea consciente de un evento.

Juan
fuente
3
Para los recién llegados a este tema, la segunda respuesta es la mejor. Desplácese hacia abajo ...
Stephan

Respuestas:

5

Puedes probar esto: http://developer.android.com/reference/java/util/Observer.html

Rui Peres
fuente
42
La respuesta de Shiki a continuación es mucho mejor.
dsaff
5
@dsaff a pesar de ser una respuesta más completa, de ninguna manera mi respuesta es incorrecta, claramente no merezco un -1. Lo que sí tiene sentido es que hagas +1 en la respuesta de Shiki.
Rui Peres
4
Shiki es la mejor respuesta para la pregunta
Ramz
4
Tenga en cuenta que solo las respuestas técnicamente incorrectas y de spam deben votarse negativamente; esta no encaja en ninguna. +1 para compensación y +1 para Shiki también porque esa es una gran respuesta.
351

El mejor equivalente que encontré es LocalBroadcastManager, que forma parte del paquete de soporte de Android .

De la documentación de LocalBroadcastManager:

Ayudante para registrarse y enviar transmisiones de Intents a objetos locales dentro de su proceso. Esto tiene una serie de ventajas sobre el envío de transmisiones globales con sendBroadcast (Intent):

  • Usted sabe que los datos que está transmitiendo no saldrán de su aplicación, por lo que no debe preocuparse por filtrar datos privados.
  • No es posible que otras aplicaciones envíen estas transmisiones a su aplicación, por lo que no debe preocuparse por los agujeros de seguridad que puedan explotar.
  • Es más eficiente que enviar una transmisión global a través del sistema.

Al usar esto, puede decir que an Intentes equivalente a an NSNotification. Aquí hay un ejemplo:

ReceiverActivity.java

Una actividad que busca notificaciones del evento nombrado "custom-event-name".

@Override
public void onCreate(Bundle savedInstanceState) {

  ...
  
  // Register to receive messages.
  // This is just like [[NSNotificationCenter defaultCenter] addObserver:...]
  // We are registering an observer (mMessageReceiver) to receive Intents
  // with actions named "custom-event-name".
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("custom-event-name"));
}

// Our handler for received Intents. This will be called whenever an Intent
// with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Get extra data included in the Intent
    String message = intent.getStringExtra("message");
    Log.d("receiver", "Got message: " + message);
  }
};

@Override
protected void onDestroy() {
  // Unregister since the activity is about to be closed.
  // This is somewhat like [[NSNotificationCenter defaultCenter] removeObserver:name:object:] 
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onDestroy();
}

SenderActivity.java

La segunda actividad que envía / difunde notificaciones.

@Override
public void onCreate(Bundle savedInstanceState) {
  
  ...
  
  // Every time a button is clicked, we want to broadcast a notification.
  findViewById(R.id.button_send).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      sendMessage();
    }
  });
}

// Send an Intent with an action named "custom-event-name". The Intent sent should 
// be received by the ReceiverActivity.
private void sendMessage() {
  Log.d("sender", "Broadcasting message");
  Intent intent = new Intent("custom-event-name");
  // You can also include some extra data.
  intent.putExtra("message", "This is my message!");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

Con el código anterior, cada vez que R.id.button_sendse hace clic en el botón , se emite un Intent y es recibido por mMessageReceiverin ReceiverActivity.

La salida de depuración debería verse así:

01-16 10:35:42.413: D/sender(356): Broadcasting message
01-16 10:35:42.421: D/receiver(356): Got message: This is my message! 
Shiki
fuente
11
Muchas gracias por tomarse el tiempo de escribir una respuesta tan útil y detallada.
Chris Lacy
14
Probablemente no debería llamar a registerReceiver en su método onCreate, ya que esto filtrará su actividad y su método onDestroy nunca será llamado. onResume parece una mejor opción para llamar a registerReceiver y onPause para llamar a unregisterReceiver.
Stephane JAIS
4
¡Equivalente perfecto a NSNotificationCenter, debería ser la respuesta aceptada!
Leon Storey
Me gustaría señalar que el uso de notificaciones globales puede llevarlo a un diseño desordenado. Piense en cuál sería el mejor acoplamiento entre sus componentes antes de saltar al camino más fácil. A veces es mejor usar oyentes o algo similar al patrón de delegado de iOS, y así sucesivamente.
saulobrito
Gracias, esto funcionó para mí. @Shiki, por favor, ¿crees que podrías darme tu opinión sobre esta pregunta? Stackoverflow.com/questions/25598696/…
Axel
16

Aquí hay algo similar a la respuesta de @Shiki, pero desde el ángulo de los desarrolladores de iOS y el centro de notificaciones.

Primero cree algún tipo de servicio NotificationCenter:

public class NotificationCenter {

 public static void addObserver(Context context, NotificationType notification, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).registerReceiver(responseHandler, new IntentFilter(notification.name()));
 }

 public static void removeObserver(Context context, BroadcastReceiver responseHandler) {
    LocalBroadcastManager.getInstance(context).unregisterReceiver(responseHandler);
 }

 public static void postNotification(Context context, NotificationType notification, HashMap<String, String> params) {
    Intent intent = new Intent(notification.name());
    // insert parameters if needed
    for(Map.Entry<String, String> entry : params.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        intent.putExtra(key, value);
    }
    LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
 }
}

Luego, también necesitará algún tipo de enumeración para estar seguro de errores en la codificación con cadenas - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Aquí está el uso (agregar / eliminar observadores) por ejemplo en actividades:

public class LoginActivity extends AppCompatActivity{

    private BroadcastReceiver loginResponseReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           // do what you need to do with parameters that you sent with notification

           //here is example how to get parameter "isSuccess" that is sent with notification
           Boolean result = Boolean.valueOf(intent.getStringExtra("isSuccess"));
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //subscribe to notifications listener in onCreate of activity
        NotificationCenter.addObserver(this, NotificationType.LoginResponse, loginResponseReceiver);
    }

    @Override
    protected void onDestroy() {
        // Don't forget to unsubscribe from notifications listener
        NotificationCenter.removeObserver(this, loginResponseReceiver);
        super.onDestroy();
    }
}

y aquí está finalmente cómo publicamos la notificación en NotificationCenter desde algún servicio de devolución de llamada o descanso o lo que sea:

public void loginService(final Context context, String username, String password) {
    //do some async work, or rest call etc.
    //...

    //on response, when we want to trigger and send notification that our job is finished
    HashMap<String,String> params = new HashMap<String, String>();          
    params.put("isSuccess", String.valueOf(false));
    NotificationCenter.postNotification(context, NotificationType.LoginResponse, params);
}

eso es todo, salud!

Elvis Rudonja
fuente
¡Gracias por tu solución! Descubrí que usar en Bundle paramslugar de HashMapes más conveniente para pasar parámetros de diferentes tipos. Hay una buena conexión entre Intenty Bundle:intent.putExtras(params)
zubko
4

Puede usar esto: http://developer.android.com/reference/android/content/BroadcastReceiver.html , que da un comportamiento similar.

Puede registrar receptores mediante programación a través de Context.registerReceiver (BroadcastReceiver, IntentFilter) y capturará las intenciones enviadas a través de Context.sendBroadcast (Intent).

Sin embargo, tenga en cuenta que un receptor no recibirá notificaciones si su actividad (contexto) se ha detenido.

AngraX
fuente
Una nota de diseño rápida: BroadcastReceivers y NSNotificationCenter pueden funcionar como un agregador de eventos. La ventaja sobre los delegados u observadores es que el remitente y el receptor están desacoplados (en realidad tienen un acoplamiento de mensajes o datos, pero ese es uno de los tipos de acoplamiento más débiles). Editado con corrección.
AngraX
4

Descubrí que el uso de EventBus of Guava lib es la forma más sencilla para la comunicación de estilo publicación-suscripción entre componentes sin requerir que los componentes se registren explícitamente entre sí

vea su muestra en https://code.google.com/p/guava-libraries/wiki/EventBusExplained

// Class is typically registered by the container.
class EventBusChangeRecorder {
  @Subscribe public void recordCustomerChange(ChangeEvent e) {
    recordChange(e.getChange());
  }

// somewhere during initialization
eventBus.register(this);

}

// much later
public void changeCustomer() {
  eventBus.post(new ChangeEvent("bla bla") );
} 

puede agregar esta biblioteca simplemente en Android Studio agregando una dependencia a su build.gradle:

compile 'com.google.guava:guava:17.0'
Shlomi Hasin
fuente
Más adecuado para el código lateral "modelo" que puede depender menos de la plataforma.
karmakaze
2

Kotlin : Aquí hay una versión de @ Shiki en Kotlin con un poco de refactorización en un fragmento.

  1. Registre al observador en Fragmento.

Fragment.kt

class MyFragment : Fragment() {

    private var mContext: Context? = null

    private val mMessageReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            //Do something here after you get the notification
            myViewModel.reloadData()
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)

        mContext = context
    }

    override fun onStart() {
        super.onStart()
        registerSomeUpdate()
    }

    override fun onDestroy() {
        LocalBroadcastManager.getInstance(mContext!!).unregisterReceiver(mMessageReceiver)
        super.onDestroy()
    }

    private fun registerSomeUpdate() {
        LocalBroadcastManager.getInstance(mContext!!).registerReceiver(mMessageReceiver, IntentFilter(Constant.NOTIFICATION_SOMETHING_HAPPEN))
    }

}
  1. Publique notificaciones en cualquier lugar. Solo tú necesitas el contexto.

    LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Constant.NOTIFICATION_SOMETHING_HAPPEN))```

PD :

  1. puede agregar un Constant.kt como yo para organizar bien las notificaciones. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Para el contexto en un fragmento, puede usar activity(a veces null) o me conextgusta lo que usé.
William Hu
fuente
0

Podría utilizar referencias débiles.

De esta manera, podría administrar la memoria usted mismo y agregar y eliminar observadores a su gusto.

Cuando agregaObserver, agregue estos parámetros: transmita ese contexto de la actividad en la que lo está agregando a la interfaz vacía, agregue un nombre de notificación y llame al método para ejecutar la interfaz.

El método para ejecutar la interfaz tendría una función que se llama ejecutar para devolver los datos que está pasando algo como esto

public static interface Themethodtorun {
        void run(String notification_name, Object additional_data);
    }

Cree una clase de observación que invoque una referencia con una interfaz vacía. También construya su interfaz Themethodtorun a partir del contexto que se pasa en el addobserver.

Agregue la observación a una estructura de datos.

Para llamarlo sería el mismo método, sin embargo, todo lo que necesita hacer es encontrar el nombre de notificación específico en la estructura de datos, use Themethodtorun.run (nombre_notificación, datos).

Esto enviará una devolución de llamada al lugar donde haya creado un observador con un nombre de notificación específico. ¡No olvide quitarlos cuando haya terminado!

Esta es una buena referencia para referencias débiles.

http://learningviacode.blogspot.co.nz/2014/02/weak-references-in-java.html

Estoy en el proceso de cargar este código en github. ¡Mantén los ojos abiertos!

Victor Du Preez
fuente