Android entspricht NSNotificationCenter


95

Während ich eine iPhone-Anwendung auf Android portiere, suche ich nach dem besten Weg, um innerhalb der App zu kommunizieren. Absichten scheinen der richtige Weg zu sein. Ist dies die beste (einzige) Option? NSUserDefaults scheint sowohl in Bezug auf die Leistung als auch in Bezug auf die Codierung viel leichter zu sein als Intents.

Ich sollte auch hinzufügen, dass ich eine Anwendungsunterklasse für den Status habe, aber ich muss eine andere Aktivität auf ein Ereignis aufmerksam machen.


3
Für Neulinge in diesem Thema ist die zweite Antwort die beste. Scrollen Sie nach unten ...
Stephan

Antworten:


5

42
Shikis Antwort unten ist viel besser.
Dsaff

5
@dsaff Obwohl es sich um eine vollständigere Antwort handelt, ist meine Antwort in keiner Weise falsch. Ich verdiene eindeutig keine -1. Was Sinn macht, ist für dich, +1 Shikis Antwort zu geben.
Rui Peres

4
Shiki ist die bessere Antwort auf die Frage
Ramz

4
Beachten Sie, dass nur technisch inkorrekte und Spam-Antworten herabgestuft werden sollten - diese passt in keine. +1 für die Entschädigung und +1 für Shiki, denn das ist eine großartige Antwort.

350

Das beste Äquivalent, das ich gefunden habe, ist LocalBroadcastManager, das Teil des Android-Support-Pakets ist .

Aus der LocalBroadcastManager-Dokumentation:

Helfer beim Registrieren und Senden von Intent-Sendungen an lokale Objekte in Ihrem Prozess. Dies hat eine Reihe von Vorteilen gegenüber dem Senden globaler Sendungen mit sendBroadcast (Intent):

  • Sie wissen, dass die von Ihnen gesendeten Daten Ihre App nicht verlassen. Sie müssen sich also keine Sorgen machen, dass private Daten verloren gehen.
  • Andere Anwendungen können diese Broadcasts nicht an Ihre App senden, sodass Sie sich keine Sorgen über Sicherheitslücken machen müssen, die sie ausnutzen können.
  • Es ist effizienter als das Senden einer globalen Sendung über das System.

Wenn Sie dies verwenden, können Sie sagen, dass an Intentein Äquivalent zu a ist NSNotification. Hier ist ein Beispiel:

ReceiverActivity.java

Eine Aktivität, die nach Benachrichtigungen für das genannte Ereignis sucht "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

Die zweite Aktivität, die Benachrichtigungen sendet / sendet.

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

Mit dem obigen Code wird jedes Mal, wenn auf die Schaltfläche R.id.button_sendgeklickt wird, eine Absicht gesendet und von mMessageReceiverin empfangen ReceiverActivity.

Die Debug-Ausgabe sollte folgendermaßen aussehen:

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
Vielen Dank, dass Sie sich die Zeit genommen haben, eine so hilfreiche und detaillierte Antwort zu schreiben.
Chris Lacy

14
Sie sollten registerReceiver wahrscheinlich nicht in Ihrer onCreate-Methode aufrufen, da dies Ihre Aktivität verliert und Ihre onDestroy-Methode niemals aufgerufen wird. onResume scheint die bessere Wahl zu sein, um registerReceiver aufzurufen, und onPause, um unregisterReceiver aufzurufen.
Stephane JAIS

4
Perfektes Äquivalent zu NSNotificationCenter, sollte die akzeptierte Antwort sein!
Leon Storey

Ich möchte darauf hinweisen, dass die Verwendung globaler Benachrichtigungen zu einem durcheinandergebrachten Design führen kann. Überlegen Sie, was die beste Kopplung zwischen Ihren Komponenten wäre, bevor Sie auf einfache Weise loslegen. Manchmal ist es einfach besser, Listener oder ähnliches wie das iOS-Delegatenmuster zu verwenden und so weiter.
Saulobrito

Danke, das hat bei mir funktioniert. @ Shiki bitte denkst du, du könntest mir deine Meinung zu dieser Frage geben stackoverflow.com/questions/25598696/…
Axel

16

Hier ist etwas ähnlich wie bei @Shiki, aber aus der Sicht von iOS-Entwicklern und Notification Center.

Erstellen Sie zunächst eine Art NotificationCenter-Dienst:

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

Dann benötigen Sie auch einen Aufzählungstyp, um Fehler beim Codieren mit Zeichenfolgen zu vermeiden - (NotificationType):

public enum NotificationType {

   LoginResponse;
   // Others

}

Hier ist die Verwendung (Hinzufügen / Entfernen von Beobachtern) zum Beispiel in Aktivitäten:

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

und hier ist schließlich, wie wir Benachrichtigungen an NotificationCenter von einem Rückruf- oder Restdienst oder was auch immer senden:

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

das war's, Prost!


Vielen Dank für Ihre Lösung! Ich fand, dass die Verwendung von Bundle paramsanstelle von HashMapbequemer ist, um Parameter verschiedener Typen zu übergeben. Es gibt eine schöne Verbindung zwischen Intentund Bundle:intent.putExtras(params)
zubko

4

Sie könnten dies verwenden: http://developer.android.com/reference/android/content/BroadcastReceiver.html , was ein ähnliches Verhalten ergibt.

Sie können Empfänger programmgesteuert über Context.registerReceiver (BroadcastReceiver, IntentFilter) registrieren und über Context.sendBroadcast (Intent) gesendete Absichten erfassen.

Beachten Sie jedoch, dass ein Empfänger keine Benachrichtigungen erhält, wenn seine Aktivität (sein Kontext) angehalten wurde.


Ein kurzer Hinweis zum Entwurf: BroadcastReceivers und NSNotificationCenter können beide als Ereignisaggregator fungieren. Der Vorteil gegenüber Delegierten oder Beobachtern besteht darin, dass Sender und Empfänger entkoppelt sind (sie haben tatsächlich eine Nachrichten- oder Datenkopplung, aber das ist einer der schwächsten Kopplungstypen). Mit Korrektur bearbeitet.
AngraX

4

Ich fand heraus, dass die Verwendung von EventBus von Guava lib der einfachste Weg für die Kommunikation zwischen Komponenten im Publish-Subscribe-Stil ist, ohne dass sich die Komponenten explizit untereinander registrieren müssen

Das Beispiel finden Sie unter 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") );
} 

Sie können diese Bibliothek einfach in Android Studio hinzufügen, indem Sie Ihrem build.gradle eine Abhängigkeit hinzufügen:

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

Eher geeignet für "Modell" -Seitencode, der weniger plattformabhängig sein kann.
Karmakaze

2

Kotlin : Hier ist eine @ Shiki-Version in Kotlin mit einem kleinen Refactor in einem Fragment.

  1. Registrieren Sie den Beobachter 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. Benachrichtigung überall posten. Nur du brauchst den Kontext.

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

PS :

  1. Sie können eine Constant.kt wie mich hinzufügen, um die Benachrichtigungen gut zu organisieren. Constant.kt
object Constant {
    const val NOTIFICATION_SOMETHING_HAPPEN = "notification_something_happened_locally"
}
  1. Für den Kontext in einem Fragment können Sie activity(manchmal null) verwenden oder conextmögen, was ich verwendet habe.

0

Sie könnten schwache Referenzen verwenden.

Auf diese Weise können Sie den Speicher selbst verwalten und Beobachter nach Belieben hinzufügen und entfernen.

Wenn SieObserver hinzufügen, fügen Sie diese Parameter hinzu - wandeln Sie diesen Kontext aus der Aktivität, in die Sie ihn einfügen, in die leere Schnittstelle um, fügen Sie einen Benachrichtigungsnamen hinzu und rufen Sie die Methode zum Ausführen der Schnittstelle auf.

Die Methode zum Ausführen der Schnittstelle verfügt über eine Funktion namens run, um die Daten zurückzugeben, die Sie so etwas übergeben

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

Erstellen Sie eine Beobachtungsklasse, die eine Referenz mit einer leeren Schnittstelle aufruft. Erstellen Sie Ihre Themethodtorun-Schnittstelle auch aus dem Kontext, der im Addobserver übergeben wird.

Fügen Sie die Beobachtung einer Datenstruktur hinzu.

Um es aufzurufen, ist es dieselbe Methode. Sie müssen jedoch nur den spezifischen Benachrichtigungsnamen in der Datenstruktur finden und die Datei Themethodtorun.run (Benachrichtigungsname, Daten) verwenden.

Dadurch wird ein Rückruf an die Stelle gesendet, an der Sie einen Beobachter mit einem bestimmten Benachrichtigungsnamen erstellt haben. Vergessen Sie nicht, sie zu entfernen, wenn Sie fertig sind!

Dies ist eine gute Referenz für schwache Referenzen.

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

Ich bin gerade dabei, diesen Code auf github hochzuladen. Augen offen halten!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.