2015-09-24 20 views
9

Inizio a imparare RxJava e mi piace così tanto. Ho un frammento che comunica con un'attività sul clic del pulsante (per sostituire il frammento corrente con un nuovo frammento). Google consiglia interface affinché i frammenti comunichino fino all'attività ma è troppo prolisso, ho provato a utilizzare il ricevitore broadcast che funziona in generale ma ha degli svantaggi.RxJava come bus eventi?

Dal momento che sto imparando RxJava mi chiedo se sia una buona opzione per comunicare dai frammenti alle attività (o frammentare in frammento) ?. In tal caso, qual è il modo migliore per utilizzare RxJava per questo tipo di comunicazione ?. Devo creare un bus eventi come questo one e se questo è il caso dovrei creare una singola istanza del bus e usarla globalmente (con i soggetti)?

risposta

9

Sì ed è davvero sorprendente dopo aver appreso come farlo. Si consideri il seguente classe Singleton:

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

} 

Nella vostra attività sia pronta a ricevere gli eventi (ad esempio, ce l'ha nel 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 te Frammento tramandare l'evento quando si verifica:

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

L'attività quindi farà qualcosa.

Suggerimento 1: modifica la stringa con qualsiasi tipo di oggetto che ti piace.

Suggerimento 2: Funziona anche molto bene se si dispone di un'iniezione di dipendenza.


Aggiornamento: ho scritto una più lunga article

+1

1. Il tuo esempio sarebbe meglio con un "BehaviorSubject" per rendere l'osservazione "appiccicosa". Ad esempio, le nuove iscrizioni vedranno l'ultimo valore. Probabilmente lo vorrai, in quanto le nuove viste che dipendono dal valore si aggiorneranno correttamente al momento dell'iscrizione. 2. 'UsernameModel' fa solo una cosa: avvolge un' PublishSubject' e lo rende un singleton. DI offre singleton! Se stavi usando DI, dovresti semplicemente iniettare direttamente "PublishSubject" o "BehaiorSubject". Se stai usando Dagger, puoi usare '@Named (" username ")' per iniettare 'Subject'. – Andy

+0

implementazione simile [controlla sotto la risposta] (https://stackoverflow.com/a/48300275/4647628) – Aks4125

2

Attualmente mi che il mio approccio preferito a questa domanda è questo:

1.) Invece di un bus globale che Gestisce tutto in tutta l'app (e di conseguenza diventa piuttosto ingombrante) usa bus "locali" per scopi chiaramente definiti e li collega solo dove ti serve.

Per esempio si potrebbe avere:

  • Un bus per la trasmissione dei dati tra il Activity s e la vostra ApiService.
  • Un bus per la comunicazione tra più Fragment s in un Activity.
  • Un bus che invia il colore del tema dell'app attualmente selezionato a tutti gli Activity in modo che possano tingere tutte le icone di conseguenza.

2.) Utilizzare pugnale (o forse AndroidAnnotations se si preferisce che) per rendere il cablaggio-tutto-insieme un po 'meno doloroso (e per evitare anche un sacco di static istanze). Questo rende anche più facile, e. g. avere un singolo componente che si occupa solo di memorizzare e leggere lo stato di accesso nel SharedPreferences - questo componente potrebbe quindi essere collegato direttamente al tuo ApiService per fornire il token di sessione per tutte le richieste.

3.) Sentiti libero di usare internamente i numeri Subject ma "trasmetterli" a Observable prima di consegnarli al pubblico chiamando lo return subject.asObservable(). Questo impedisce ad altre classi di spingere valori nello Subject dove non dovrebbero essere autorizzati.

+1

Davvero mi chiedo, qual è il punto di avere più istanze di un bus evento? Non contribuisce ad un'elevata coesione. Invece di un'istanza, si finiranno con più istanze della stessa cosa senza alcun beneficio reale. – Gunhan

+0

@Gunhan Meno casting in giro. Un bus specifico ha uno specifico modello di comunicazione. Se è necessario ascoltare le modifiche "SomeObject", perché inviare SomeObjects tramite lo stesso bus in cui "SomeOtherObject" potrebbe essere in viaggio. Gli autobus in cemento sono corsie multi-strato dedicate di informazioni specifiche. –

0

definire gli eventi

public class Trigger { 

public Trigger() { 
} 

public static class Increment { 
} 

public static class Decrement { 
} 

public static class Reset { 
} 
} 

regolatore Evento

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


} 

evento Registrati ascoltatore laddove richiesto

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

caso di invio/Fuoco

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

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

La piena attuazione di sopra biblioteca con l'esempio ->RxTrigger

0.123.