J'utilise Firebase dans mon application, avec RxJava . Firebase est capable de notifier votre application chaque fois que quelque chose a changé dans les données principales (ajout, suppressions, modifications, ...) . Je suis essayant de combiner la fonctionnalité de Firebase avec RxJava.
Les données que j'écoute s'appellent Leisure
et la Observable
émet LeisureUpdate
qui contient une Leisure
et le type de mise à jour (ajout, suppression, déplacement, modification).
Voici ma méthode qui permet de s'abonner à ces événements.
private Observable<LeisureUpdate> leisureUpdatesObservable;
private ChildEventListener leisureUpdatesListener;
private int leisureUpdatesSubscriptionsCount;
@NonNull
public Observable<LeisureUpdate> subscribeToLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
leisureUpdatesObservable = Observable.create(new Observable.OnSubscribe<LeisureUpdate>() {
@Override
public void call(final Subscriber<? super LeisureUpdate> subscriber) {
leisureUpdatesListener = firebase.child(FirebaseStructure.LEISURES).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.ADDED));
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.CHANGED));
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.REMOVED));
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.MOVED));
}
@Override
public void onCancelled(FirebaseError firebaseError) {
subscriber.onError(new Error(firebaseError.getMessage()));
}
});
}
});
}
leisureUpdatesSubscriptionsCount++;
return leisureUpdatesObservable;
}
Tout d'abord, j'aimerais utiliser la méthode Observable.fromCallable()
afin de créer la Observable
, mais je suppose que c'est impossible, puisque Firebase utilise des rappels, n'est-ce pas?
Je garde une seule instance de Observable
afin d'avoir toujours une Observable
où plusieurs Subscriber
peuvent s'abonner.
Le problème survient lorsque tout le monde se désabonne et que je dois cesser d'écouter les événements dans Firebase . Je n'ai de toute façon pas réussi à faire comprendre à la Observable
s'il y a encore un abonnement. Je continue donc à compter le nombre d'appels auxquels je suis arrivé à subscribeToLeisuresUpdates()
, avec leisureUpdatesSubscriptionsCount
.
Ensuite, chaque fois que quelqu'un veut se désabonner, il doit appeler
@Override
public void unsubscribeFromLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
return;
}
leisureUpdatesSubscriptionsCount--;
if (leisureUpdatesSubscriptionsCount == 0) {
firebase.child(FirebaseStructure.LEISURES).removeEventListener(leisureUpdatesListener);
leisureUpdatesObservable = null;
}
}
C’est le seul moyen que j’ai trouvé pour faire en sorte que la Observable
émette des éléments s’il ya un abonné, mais j’ai le sentiment qu’il doit exister un moyen plus simple, particulièrement de comprendre quand aucun autre abonné n’écoute l'observable.
Toute personne ayant rencontré un problème similaire ou ayant une approche différente?
Vous pouvez utiliser Observable.fromEmitter, quelque chose dans ce sens
return Observable.fromEmitter(new Action1<Emitter<LeisureUpdate>>() {
@Override
public void call(final Emitter<LeisureUpdate> leisureUpdateEmitter) {
final ValueEventListener listener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// process update
LeisureUpdate leisureUpdate = ...
leisureUpdateEmitter.onNext(leisureUpdate);
}
@Override
public void onCancelled(DatabaseError databaseError) {
leisureUpdateEmitter.onError(new Throwable(databaseError.getMessage()));
mDatabaseReference.removeEventListener(this);
}
};
mDatabaseReference.addValueEventListener(listener);
leisureUpdateEmitter.setCancellation(new Cancellable() {
@Override
public void cancel() throws Exception {
mDatabaseReference.removeEventListener(listener);
}
});
}
}, Emitter.BackpressureMode.BUFFER);
Je vous suggère de vérifier (ou tout simplement de l'utiliser) l'une des bibliothèques suivantes:
RxJava: https://github.com/nmoskalenko/RxFirebase
RxJava 2.0: https://github.com/FrangSierra/Rx2Firebase
L'un d'eux fonctionne avec RxJava et l'autre avec le nouveau RC de RxJava 2.0. Si vous êtes intéressé, vous pouvez voir les différences entre les deux ici .
Mettez ceci dans votre Observable.create () à la fin.
subscriber.add(Subscriptions.create(new Action0() {
@Override public void call() {
ref.removeEventListener(leisureUpdatesListener);
}
}));
Une autre bibliothèque étonnante qui vous aidera à envelopper toute la logique de base de données en temps réel de firebase sous des modèles rx.
https://github.com/Link184/Respiration
Ici, vous pouvez créer votre référentiel firebase et l'étendre à partir de GeneralRepository, par exemple:
@RespirationRepository(dataSnapshotType = Leisure.class)
public class LeisureRepository extends GeneralRepository<Leisure>{
protected LeisureRepository(Configuration<Leisure> repositoryConfig) {
super(repositoryConfig);
}
// observable will never emit any events if there are no more subscribers
public void killRepository() {
if (!behaviorSubject.hasObservers()) {
//protected fields
behaviorSubject.onComplete();
databaseReference.removeEventListener(valueListener);
}
}
}
Vous pouvez "tuer" votre dépôt de cette manière:
// LeisureRepositoryBuilder is a generated class by annotation processor, will appear after a successful gradle build
LeisureRepositoryBuilder.getInstance().killRepository();
Mais je pense que votre situation sera préférable d’étendre com.link184.respiration.repository.ListRepository pour éviter le mappage des données de Java.util.Map au modèle Leisure via LeisureUpdate
Voici un exemple de code d'utilisation de RxJava2 avec CompletionListener de Firebase:
Completable.create(new CompletableOnSubscribe() {
@Override
public void subscribe(final CompletableEmitter e) throws Exception {
String orderKey = FirebaseDatabase.getInstance().getReference().child("orders").Push().getKey();
FirebaseDatabase.getInstance().getReference().child("orders").child(orderKey).setValue(order,
new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (e.isDisposed()) {
return;
}
if (databaseError == null) {
e.onComplete();
} else {
e.onError(new Throwable(databaseError.getMessage()));
}
}
});
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());