Android équivalent à NSNotificationCenter


95

En train de porter une application iPhone sur Android, je recherche le meilleur moyen de communiquer au sein de l'application. Les intentions semblent être la voie à suivre, est-ce la meilleure (seule) option? NSUserDefaults semble beaucoup plus léger que Intents en termes de performances et de codage.

Je devrais également ajouter que j'ai une sous-classe d'application pour l'état, mais je dois rendre une autre activité consciente d'un événement.


3
Pour les nouveaux venus sur ce sujet, la deuxième réponse est la meilleure. Faites défiler vers le bas ...
Stephan

Réponses:


5

42
La réponse de Shiki ci-dessous est bien meilleure.
dsaff

5
@dsaff en dépit d'être une réponse plus complète, en aucun cas ma réponse n'est fausse, je ne mérite clairement pas un -1. Ce qui a du sens, c'est que vous attribuiez +1 à la réponse de Shiki.
Rui Peres

4
Shiki est la meilleure réponse à la question
Ramz

4
Notez que seules les réponses techniquement incorrectes et les spams doivent être rejetées - celle-ci ne correspond à aucune des deux. +1 pour la compensation et +1 pour Shiki aussi parce que c'est une excellente réponse.

351

Le meilleur équivalent que j'ai trouvé est LocalBroadcastManager qui fait partie du package de support Android .

À partir de la documentation de LocalBroadcastManager:

Aide pour vous inscrire et envoyer des diffusions d'intentions aux objets locaux au sein de votre processus. Cela présente un certain nombre d'avantages par rapport à l'envoi de diffusions mondiales avec sendBroadcast (Intent):

  • Vous savez que les données que vous diffusez ne quitteront pas votre application, alors ne vous inquiétez pas des fuites de données privées.
  • Il n'est pas possible pour d'autres applications d'envoyer ces diffusions à votre application, vous n'avez donc pas à vous soucier des failles de sécurité qu'elles peuvent exploiter.
  • C'est plus efficace que d'envoyer une diffusion globale via le système.

Lorsque vous l'utilisez, vous pouvez dire que an Intentest un équivalent à un NSNotification. Voici un exemple:

ReceiverActivity.java

Une activité qui surveille les notifications de l'événement nommé "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 deuxième activité qui envoie / diffuse des notifications.

@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);
}

Avec le code ci-dessus, chaque fois que vous R.id.button_sendcliquez sur le bouton , une intention est diffusée et reçue par mMessageReceiverin ReceiverActivity.

La sortie de débogage devrait ressembler à ceci:

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! 

11
Merci beaucoup d'avoir pris le temps de rédiger une réponse aussi utile et détaillée.
Chris Lacy

14
Vous ne devriez probablement pas appeler registerReceiver dans votre méthode onCreate car cela entraînera une fuite de votre activité et votre méthode onDestroy ne sera jamais appelée. onResume semble un meilleur choix pour appeler registerReceiver et onPause pour appeler unregisterReceiver.
Stephane JAIS

4
Parfait équivalent à NSNotificationCenter, devrait être la réponse acceptée!
Leon Storey

Je tiens à souligner que l'utilisation de notifications globales peut vous conduire à une conception en désordre. Pensez à quel serait le meilleur couplage entre vos composants avant de passer à la solution de facilité. Parfois, il est préférable d'utiliser des écouteurs ou quelque chose de similaire au modèle de délégué iOS, etc.
saulobrito

Merci, cela a fonctionné pour moi. @Shiki s'il vous plaît pensez-vous que vous pourriez me donner votre avis sur cette question stackoverflow.com/questions/25598696/…
Axel

16

Voici quelque chose de similaire à la réponse @Shiki, mais sous l'angle des développeurs iOS et du centre de notification.

Créez d'abord une sorte de service 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);
 }
}

Ensuite, vous aurez également besoin d'un type enum pour être sûr des erreurs de codage avec des chaînes - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Voici l'utilisation (ajouter / supprimer des observateurs) par exemple dans les activités:

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

et voici enfin comment nous postons une notification à NotificationCenter à partir d'un service de rappel ou de repos ou autre:

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

ça y est, bravo!


Merci pour votre solution! J'ai trouvé que l'utilisation Bundle paramsau lieu de HashMapest plus pratique pour passer des paramètres de types différents. Il y a une bonne connexion entre Intentet Bundle:intent.putExtras(params)
zubko

4

Vous pouvez utiliser ceci: http://developer.android.com/reference/android/content/BroadcastReceiver.html , ce qui donne un comportement similaire.

Vous pouvez enregistrer des destinataires par programme via Context.registerReceiver (BroadcastReceiver, IntentFilter) et il capturera les intentions envoyées via Context.sendBroadcast (Intent).

Notez cependant qu'un récepteur ne recevra pas de notifications si son activité (contexte) a été suspendue.


Une note de conception rapide: BroadcastReceivers et NSNotificationCenter peuvent tous deux fonctionner comme un agrégateur d'événements. L'avantage par rapport aux délégués ou aux observateurs est que l'émetteur et le récepteur sont découplés (ils ont en fait un couplage de messages ou de données, mais c'est l'un des types de couplage les plus faibles). Édité avec correction.
AngraX du

4

J'ai trouvé que l'utilisation d'EventBus de Guava lib est le moyen le plus simple pour la communication de type publication-abonnement entre les composants sans exiger que les composants s'enregistrent explicitement les uns avec les autres

voir leur exemple sur 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") );
} 

vous pouvez ajouter cette bibliothèque simplement sur Android Studio en ajoutant une dépendance à votre build.gradle:

compile 'com.google.guava:guava:17.0'

Plus approprié pour le code côté «modèle» qui peut être moins dépendant de la plateforme.
karmakaze

2

Kotlin : Voici une version de @ Shiki dans Kotlin avec un petit refactor dans un fragment.

  1. Enregistrez l'observateur dans Fragment.

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. Publiez une notification n'importe où. Seulement vous avez besoin du contexte.

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

PS :

  1. vous pouvez ajouter un Constant.kt comme moi pour bien organiser les notifications. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Pour le contexte dans un fragment, vous pouvez utiliser activity(parfois null) ou conextaimer ce que j'ai utilisé.

0

Vous pouvez utiliser des références faibles.

De cette façon, vous pouvez gérer vous-même la mémoire et ajouter et supprimer des observateurs à votre guise.

Lorsque vous addObserver ajoutez ces paramètres - transtypez ce contexte à partir de l'activité dans laquelle vous l'ajoutez dans l'interface vide, ajoutez un nom de notification et appelez la méthode pour exécuter l'interface.

La méthode pour exécuter l'interface aurait une fonction appelée run pour renvoyer les données que vous transmettez quelque chose comme ça

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

Créez une classe d'observation qui appelle une référence avec une interface vide. Construisez également votre interface Themethodtorun à partir du contexte transmis dans l'addobserver.

Ajoutez l'observation à une structure de données.

L'appeler serait la même méthode, mais tout ce que vous avez à faire est de trouver le nom de notification spécifique dans la structure de données, utilisez le Themethodtorun.run (notification_name, data).

Cela enverra un rappel à l'endroit où vous avez créé un observateur avec un nom de notification spécifique. N'oubliez pas de les supprimer lorsque vous avez terminé!

C'est une bonne référence pour les références faibles.

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

Je suis en train de télécharger ce code sur github. Gardez les yeux ouverts!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.