2015-09-24 23 views
9

Ich fange an, RxJava zu lernen und ich mag es so weit. Ich habe ein Fragment, das mit einer Aktivität auf Knopfdruck kommuniziert (um das aktuelle Fragment durch ein neues Fragment zu ersetzen). Google empfiehlt interface für Fragmente, um mit der Aktivität zu kommunizieren, aber es ist zu ausführlich, ich habe versucht, Broadcast-Empfänger zu verwenden, der allgemein funktioniert, aber es hatte Nachteile.RxJava als Eventbus?

Da ich RxJava lerne, frage ich mich, ob es eine gute Option ist, von Fragmenten zu Aktivitäten (oder von Fragment zu Fragment) zu kommunizieren. Wenn ja, was ist der beste Weg, RxJava für diese Art von Kommunikation zu verwenden ?. Muss ich Eventbus wie diesen machen one und wenn das der Fall ist sollte ich eine einzelne Instanz des Busses machen und global (mit Subjekten) benutzen?

Antwort

9

Ja und es ist ziemlich erstaunlich, nachdem Sie lernen, wie es geht. Betrachten Sie die folgenden Singletonklasse:

public class UsernameModel { 

    private static UsernameModel instance; 

    private PublishSubject<String> subject = PublishSubject.create(); 

    public static UsernameModel instanceOf() { 
     if (instance == null) { 
      instance = new UsernameModel(); 
     } 
     return instance; 
    } 

    /** 
    * Pass a String down to event listeners. 
    */ 
    public void setString(String string) { 
     subject.onNext(string); 
    } 

    /** 
    * Subscribe to this Observable. On event, do something e.g. replace a fragment 
    */ 
    public Observable<String> getStringObservable() { 
     return subject; 
    } 

} 

in Ihrer Tätigkeit bereit sein, Ereignisse zu empfangen (zum Beispiel hat es in der onCreate):

UsernameModel usernameModel = UsernameModel.instanceOf(); 

//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy 
subscription = usernameModel.getStringObservable() 
     .subscribe(s -> { 
      // Do on new string event e.g. replace fragment here 
     }, throwable -> { 
      // Normally no error will happen here based on this example. 
     }); 

In Ihrem Fragmente passiert das Ereignis ab, wenn sie auftritt:

UsernameModel.instanceOf().setString("Nick"); 

Ihre Aktivität wird dann etwas tun.

Tipp 1: Ändern Sie die Zeichenfolge mit einem beliebigen Objekttyp.

Tipp 2: Es funktioniert auch, wenn Sie Dependency-Injektion haben.


Update: Ich schrieb einen langen article

+1

1. Ihr Beispiel wäre besser mit einem "BehaviorSubject", um dieses "sticky" zu beobachten. d. h. neue Abonnements sehen den letzten Wert. Wahrscheinlich möchten Sie, dass neue Ansichten, die von diesem Wert abhängig sind, beim Abonnement korrekt aktualisiert werden. 2. 'UsernameModel' macht nur eine Sache: Wrappt ein' PublishSubject' und macht es zu einem Singleton. DI bietet Singletons an! Wenn Sie DI verwenden, würden Sie direkt das 'PublishSubject' oder' BehaiorSubject' injizieren. Wenn Sie Dolch verwenden, können Sie '@Named (" username ")' verwenden, um das 'Subject' zu injizieren. – Andy

+0

ähnliche Implementierung [Überprüfen Sie unter Antwort] (https://Stackoverflow.com/a/48300275/4647628) – Aks4125

2

Derzeit Ich denke mein bevorzugter Ansatz auf diese Frage ist dies:

1.) Anstelle einen globalen Bus, handhabt alles in der App (und wird dadurch ziemlich unhandlich) verwenden "lokale" Busse für klar definierte Zwecke und stecken sie nur dort ein, wo sie gebraucht werden.

Zum Beispiel könnten Sie haben:

  • Ein Bus für Daten zwischen Ihrem Activity s und Ihre ApiService senden.
  • Ein Bus für die Kommunikation zwischen mehreren Fragment s in einem Activity.
  • Ein Bus, der die aktuell ausgewählte App-Designfarbe an alle Activity s sendet, so dass sie alle Symbole entsprechend tönen können.

2.) Verwenden Dagger (oder vielleicht AndroidAnnotations, wenn Sie das) bevorzugen die Verkabelung-alles-zusammen ein bisschen weniger schmerzhaft zu machen (und auch viele static Instanzen zu vermeiden). Dies macht es auch einfacher, z. G. Haben Sie eine einzige Komponente, die nur mit dem Speichern und Lesen des Anmeldestatus in der SharedPreferences beschäftigt - diese Komponente könnte dann auch direkt an Ihre ApiService verdrahtet werden, um das Sitzungstoken für alle Anfragen bereitzustellen.

3.) Fühlen Sie sich frei, Subject s intern zu benutzen, aber werfen Sie sie zu Observable, bevor Sie sie der Öffentlichkeit durch das Anrufen return subject.asObservable() austeilen. Dies verhindert, dass andere Klassen Werte in die Subject schieben, wo sie nicht erlaubt sind.

+1

Wirklich frage ich mich, was ist der Punkt, mehrere Instanzen eines Event-Bus zu haben? Es trägt nicht zu einem hohen Zusammenhalt bei. Statt einer Instanz enden mehrere Instanzen derselben Sache ohne wirklichen Nutzen. – Gunhan

+0

@Gunhan Weniger Casting herum. Ein bestimmter Bus hat ein spezifisches Kommunikationsmodell. Wenn Sie nach "SomeObject" -Änderungen Ausschau halten müssen, sollten Sie SomeObjects über denselben Bus senden, auf dem sich "SomeOtherObject" befindet. Betonbusse sind dedizierte mehrschichtige Bahnen spezifischer Informationen. –

0

definieren Ereignisse

public class Trigger { 

public Trigger() { 
} 

public static class Increment { 
} 

public static class Decrement { 
} 

public static class Reset { 
} 
} 

Ereigniscontroller

public class RxTrigger { 

private PublishSubject<Object> mRxTrigger = PublishSubject.create(); 

public RxTrigger() { 
    // required 
} 

public void send(Object o) { 
    mRxTrigger.onNext(o); 
} 

public Observable<Object> toObservable() { 
    return mRxTrigger; 
} 
// check for available events 
public boolean hasObservers() { 
    return mRxTrigger.hasObservers(); 
} 
} 

Application.class

public class App extends Application { 

private RxTrigger rxTrigger; 

public App getApp() { 
    return (App) getApplicationContext(); 
} 

@Override 
public void onCreate() { 
    super.onCreate(); 
    rxTrigger = new RxTrigger(); 
} 


public RxTrigger reactiveTrigger() { 
    return rxTrigger; 
} 


} 

Register Ereignis-Listener, wo immer erforderlich

   MyApplication mApp = (App) getApplicationContext(); 
       mApp 
        .reactiveTrigger() // singleton object of trigger 
        .toObservable() 
        .subscribeOn(Schedulers.io()) // push to io thread 
        .observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread 
        .subscribe(object -> { //receive events here 
         if (object instanceof Trigger.Increment) { 
          fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1)); 
         } else if (object instanceof Trigger.Decrement) { 
          if (Integer.parseInt(fabCounter.getText().toString()) != 0) 
           fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1)); 
         } else if (object instanceof Trigger.Reset) { 
          fabCounter.setText("0"); 
         } 
        }); 

Senden/Feuer Ereignis

MyApplication mApp = (App) getApplicationContext(); 
//increment 
mApp 
    .reactiveTrigger() 
    .send(new Trigger.Increment()); 

//decrement 
mApp 
    .reactiveTrigger() 
    .send(new Trigger.Decrement()); 

Die vollständige Umsetzung für oben Bibliothek mit Beispiel ->RxTrigger