Android equivalente a NSNotificationCenter


95

Nel processo di porting di un'applicazione iPhone su Android, sto cercando il modo migliore per comunicare all'interno dell'app. Gli intenti sembrano essere la strada da percorrere, è questa la migliore (unica) opzione? NSUserDefaults sembra molto più leggero di Intents sia in termini di prestazioni che di codifica.

Dovrei anche aggiungere che ho una sottoclasse dell'applicazione per lo stato, ma devo rendere un'altra attività a conoscenza di un evento.


3
Per i nuovi arrivati ​​a questo argomento, la seconda risposta è la migliore. Scorri verso il basso ...
Stephan

Risposte:


5

42
La risposta di Shiki di seguito è molto migliore.
dsaff

5
@dsaff nonostante sia una risposta più completa, in nessun modo la mia risposta è sbagliata, chiaramente non merito un -1. Ciò che ha senso è che tu faccia +1 sulla risposta di Shiki.
Rui Peres

4
Shiki è la risposta migliore per la domanda
Ramz

4
Nota che solo le risposte tecnicamente errate e spam dovrebbero essere sottovalutate - questa non si adatta a nessuna delle due. +1 per il risarcimento e +1 anche per Shiki perché è un'ottima risposta.

351

Il miglior equivalente che ho trovato è LocalBroadcastManager che fa parte del pacchetto di supporto Android .

Dalla documentazione di LocalBroadcastManager:

Assistente per la registrazione e l'invio di trasmissioni di Intenti a oggetti locali all'interno del processo. Questo ha una serie di vantaggi rispetto all'invio di trasmissioni globali con sendBroadcast (Intent):

  • Sai che i dati che stai trasmettendo non lasceranno la tua app, quindi non devi preoccuparti di perdere dati privati.
  • Non è possibile per altre applicazioni inviare queste trasmissioni alla tua app, quindi non devi preoccuparti di avere falle di sicurezza che possono sfruttare.
  • È più efficiente dell'invio di una trasmissione globale attraverso il sistema.

Quando lo usi, puoi dire che un Intentè equivalente a un NSNotification. Ecco un esempio:

ReceiverActivity.java

Un'attività che controlla le notifiche per l'evento denominato "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 seconda attività che invia / trasmette notifiche.

@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 il codice sopra, ogni volta che si R.id.button_sendfa clic sul pulsante , viene trasmesso un Intent e viene ricevuto da mMessageReceiverin ReceiverActivity.

L'output di debug dovrebbe essere simile a questo:

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
Grazie mille per aver dedicato del tempo a scrivere una risposta così utile e dettagliata.
Chris Lacy

14
Probabilmente non dovresti chiamare registerReceiver nel tuo metodo onCreate poiché questo perderà la tua attività e il tuo metodo onDestroy non verrà mai chiamato. onResume sembra una scelta migliore per chiamare registerReceiver e onPause per chiamare unregisterReceiver.
Stephane JAIS

4
Perfetto equivalente a NSNotificationCenter, dovrebbe essere la risposta accettata!
Leon Storey

Vorrei sottolineare che l'utilizzo delle notifiche globali potrebbe portarti a un design incasinato. Pensa a quale sarebbe il miglior accoppiamento tra i tuoi componenti prima di passare alla via più semplice. A volte è semplicemente meglio usare listener o qualcosa di simile al pattern delegato iOS e così via.
saulobrito

Grazie, questo ha funzionato per me. @Shiki, per favore, pensi di potermi dare la tua opinione su questa domanda stackoverflow.com/questions/25598696/…
Axel

16

Ecco qualcosa di simile alla risposta di @Shiki, ma dal punto di vista degli sviluppatori iOS e del centro di notifica.

Per prima cosa crea una sorta di servizio 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);
 }
}

Quindi, avrai anche bisogno di un tipo di enum per essere sicuro degli errori nella codifica con le stringhe - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Ecco l'utilizzo (aggiungi / rimuovi osservatori) ad esempio nelle attività:

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

ed ecco finalmente come pubblichiamo la notifica su NotificationCenter da qualche servizio di richiamata o riposo o qualsiasi altra cosa:

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

questo è tutto, saluti!


Grazie per la tua soluzione! Ho scoperto che usare Bundle paramsinvece di HashMapè più conveniente per passare parametri di diversi tipi. C'è una bella connessione tra Intente Bundle:intent.putExtras(params)
zubko

4

Puoi usare questo: http://developer.android.com/reference/android/content/BroadcastReceiver.html , che fornisce un comportamento simile.

È possibile registrare i ricevitori a livello di codice tramite Context.registerReceiver (BroadcastReceiver, IntentFilter) e catturerà gli intenti inviati tramite Context.sendBroadcast (Intent).

Tieni presente, tuttavia, che un destinatario non riceverà notifiche se la sua attività (contesto) è stata sospesa.


Una breve nota di progettazione: BroadcastReceivers e NSNotificationCenter possono funzionare entrambi come aggregatori di eventi. Il vantaggio rispetto ai delegati o agli osservatori è che il mittente e il destinatario sono disaccoppiati (in realtà hanno un accoppiamento di messaggi o dati, ma questo è uno dei tipi di accoppiamento più deboli). Modificato con correzione.
AngraX

4

Ho scoperto che l'uso di EventBus di Guava lib è il modo più semplice per la comunicazione in stile pubblicazione-sottoscrizione tra i componenti senza richiedere che i componenti si registrino esplicitamente l'uno con l'altro

guarda il loro esempio su 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") );
} 

puoi aggiungere questa libreria semplicemente su Android Studio aggiungendo una dipendenza al tuo build.gradle:

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

Più adatto per il codice lato "modello" che può essere meno dipendente dalla piattaforma.
karmakaze

2

Kotlin : Ecco una versione di @ Shiki in Kotlin con un po 'di refactoring in un frammento.

  1. Registra l'osservatore in 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. Pubblica notifiche ovunque. Solo tu hai bisogno del contesto.

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

PS :

  1. puoi aggiungere un Constant.kt come me per organizzare bene le notifiche. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Per il contesto in un frammento, puoi usare activity(a volte null) o mi conextpiace quello che ho usato.

0

Potresti usare riferimenti deboli.

In questo modo potresti gestire tu stesso la memoria e aggiungere e rimuovere osservatori a tuo piacimento.

Quando addObserver aggiungi questi parametri, esegui il cast del contesto dall'attività in cui lo stai aggiungendo all'interfaccia vuota, aggiungi un nome di notifica e chiama il metodo per eseguire l'interfaccia.

Il metodo per eseguire l'interfaccia avrebbe una funzione chiamata run per restituire i dati che stai passando qualcosa di simile

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

Crea una classe di osservazione che richiama un riferimento con un'interfaccia vuota. Costruisci anche la tua interfaccia Themethodtorun dal contesto passato nell'addobserver.

Aggiungi l'osservazione a una struttura dati.

Per chiamarlo sarebbe lo stesso metodo, tuttavia tutto ciò che devi fare è trovare il nome di notifica specifico nella struttura dei dati, utilizzare Themethodtorun.run (notification_name, data).

Questo invierà una richiamata a dove hai creato un osservatore con un nome di notifica specifico. Non dimenticare di rimuoverli quando hai finito!

Questo è un buon riferimento per i riferimenti deboli.

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

Sto caricando questo codice su GitHub. Tieni gli occhi aperti!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.