2017-07-19 1 views
0

J'écris l'extrait de code suivant pour aller chercher la liste des aliments sauvegardés à partir de la base de données Firebase, puis en utilisant cette liste, je récupère les détails de la base de données Firebase.RxAndroid, Comment détecter si observable a fini l'émission

Le code suivant fonctionne bien, sauf que je suis incapable de comprendre comment laisser second flatMap savoir que l'émission du premier flatMap est terminée (toute la liste des aliments a été traitée). Donc, je suis incapable d'appeler la méthode onCompleted() donc incapable de détecter quand tout le processus se termine.

Jetez un oeil à des commentaires dans l'extrait suivant:

Observable.create<List<PersonalizedFood>> { 

      FirebaseDTDatabase.getSavedDietFoodQuery(user.uid).addListenerForSingleValueEvent(object : ValueEventListener { 
       override fun onCancelled(p0: DatabaseError?) { 

       } 

       override fun onDataChange(p0: DataSnapshot?) { 
        val list = ArrayList<PersonalizedFood>() 
        p0?.let { 
         for (dateObject in p0.children) { 
          for (foodItem in dateObject.children) { 
           val food = foodItem.getValue(FBPersonalizedFood::class.java) as FBPersonalizedFood 
           list.add(PersonalizedFood(food)) 
          } 
         } 
        } 
        it.onNext(list) 
        it.onCompleted() 
       } 
      }) 
     }.subscribeOn(Schedulers.io()).flatMap { 
      Observable.from(it) // returning a Observable that emits items of list ("it" is the list here) 
     }.observeOn(Schedulers.io()).flatMap { 
     // How does this flatMap know that emission of all item has been finished so that onCompleted() method could be called. 
      personalizedFood -> 

      Observable.create<Boolean>{ 
       FirebaseDTDatabase.getFoodListReference(personalizedFood.foodId).addListenerForSingleValueEvent(object :ValueEventListener{ 
        override fun onCancelled(p0: DatabaseError?) { 
         it.onError(p0?.toException()) 
        } 

        override fun onDataChange(p0: DataSnapshot?) { 
         if(p0 != null) { 
          val food = p0.getValue(FBFood::class.java)!! 
          val repo = LocalFoodRepository() 
          doAsync { 
           repo.insertFood([email protected], Food(food.foodId, food.foodName, food.foodDesc)) 
           repo.insertServingDetails([email protected], food.servingList.map { it.component2() }) 
           repo.saveFood([email protected], personalizedFood) 
           it.onNext(true) 
          } 

         }else { 
          it.onNext(false) 
         } 
        } 

       }) 
      } 
     }.observeOn(Schedulers.io()).doOnCompleted{ 
      dismissProgressDialog() 
      finish() 
     }.doOnError{ 
      it.printStackTrace() 
      dismissProgressDialog() 
      finish() 
     }.subscribe() 

Merci.

+0

Utilisez-vous fireabse? Il y a une tierce partie RxFirebase qui peut vous donner un bon wrapper pour la base de données Firebase. –

+0

@PhoenixWang C'est différent. En fait, j'apprends donc voulez savoir comment y parvenir sans utiliser de wrapper tiers. Et je ne suis pas fan de l'utilisation de la troisième partie si c'est assez facile de le faire par vous-même .. – chandil03

+0

Eh bien, en fait, vous n'avez pas besoin de. Dans le premier observable, vous récupérez tous les éléments de PersonalizedFood et les exportez sous forme de liste, puis complétez le flux. Ensuite, vous le transformez en un ensemble d'éléments, chacun d'eux est traité dans second flatMap. Le point clé est que chaque onCompleted est transmis dans le flux, donc le second flatMap "sait" qu'il n'y aura plus d'éléments et se termine. Je suggère de revoir la sécurité et la cohérence du code, car certains points me semblent erronés. – MightySeal

Répondre

2

Le Observable du flatMap sait « quand tous les éléments ont été fini » lorsque toutes les émis par elle observables ont appelé onCompleted(). Le deuxième flatMap dans votre code n'appelle jamais onCompleted() car aucun des observables qu'il crée n'appelle onCompleted(). Vous devez appeler onCompleted() dans votre méthode onDataChange(). Comme chacun des observables créés dans le flatMap n'émettent un élément, il peut être appelé directement après la méthode onNext():

override fun onDataChange(p0: DataSnapshot?) { 
    if(p0 != null) { 
     val food = p0.getValue(FBFood::class.java)!! 
     val repo = LocalFoodRepository() 
     doAsync { 
      repo.insertFood([email protected], Food(food.foodId, food.foodName, food.foodDesc)) 
      repo.insertServingDetails([email protected], food.servingList.map { it.component2() }) 
      repo.saveFood([email protected], personalizedFood) 
      it.onNext(true) 
      it.onCompleted() 
     } 
    } else { 
     it.onNext(false) 
     it.onCompleted() 
    } 
} 
+0

Cela ne répond pas à mes exigences. Je veux terminer l'activité lorsque toutes les données ont été récupérées. C'est pourquoi je ne peux pas appeler 'onComplete' sur' onDataChange() '. – chandil03

+0

@ chandil03 Ce serait la méthode 'doOnCompleted()', non? Vous appelez 'finish()' dans cette méthode, donc ajouter 'onComplete()' devrait déclencher l'activité pour finir. – Bryan

+0

Je sais que, si vous vérifiez mon code attentivement, j'ai ajouté l'appel de méthode 'finish()' dans 'doOnCompleted()'. si j'appelle 'onComplete()' lorsque vous me le suggérez, 'la méthode doOnComplete sera appelée autant de fois que beaucoup d'éléments sont émis par observable'. Donc 'finish()' sera appelé plusieurs fois par conséquent, l'appli se bloquera car l'activité sera nulle. – chandil03