Opérateurs de filtrage RxJava | raywenderlich.com

Online Coding Courses for Kids

RxJava est une bibliothèque puissante pour gérer le code asynchrone basé sur les événements. Il évite les rappels en fournissant un style de programmation composable. RxJava fournit de nombreux opérateurs polyvalents que vous pouvez utiliser pour traiter vos données de manière simple.

Dans ce didacticiel, vous découvrirez les opérateurs de filtrage RxJava, un groupe d’opérateurs puissants qui vous permettent de travailler avec les données dont vous avez besoin à une étape spécifique du processus de transformation des données.

Au cours de ce processus, vous apprendrez à:

  • Prenez un certain nombre d’éléments de votre flux de données.
  • Laissez votre flux de données ignorer les éléments en fonction d’une condition.
  • Filtrez vos données en fonction d’un prédicat.
  • Prendre un élément uniquement après que l’utilisateur a fini de fournir une entrée.
  • N’agissez sur les éléments séquentiels que lorsqu’ils diffèrent.

Ces opérateurs formeront la base de votre vocabulaire RxJava. Il est temps de plonger!

Commencer

Téléchargez le projet de démarrage en cliquant sur le Télécharger les documents en haut ou en bas du didacticiel. Ensuite, ouvrez le projet de démarrage dans Android Studio, où vous trouverez Foodeat, une application de recherche de restaurant.

Construisez et exécutez. Vous verrez l’écran suivant:


Écran de démarrage de nourriture

Vous verrez une liste des restaurants classés du plus élevé au moins bien noté. L’application stocke les restaurants dans une base de données Room. Vous verrez également un champ de recherche et un menu avec certains éléments qui représentent des critères de filtrage dans la barre d’outils.

Écran de démarrage Foodeat avec le menu ouvert

Vous allez développer des fonctionnalités pour filtrer les informations en fonction des critères donnés par le champ de recherche et les éléments de menu. Avec ces informations, vous choisirez rapidement un endroit où manger. :]

Avant de commencer à coder, prenez un moment pour revoir une théorie sur ce qu’est RxJava.

Qu’est-ce que RxJava?

RxJava est une bibliothèque qui étend le modèle d’observateur pour le traitement de code asynchrone en utilisant des séquences et des opérateurs de style fonctionnels. RxJava offre plusieurs avantages. Avec lui, vous pouvez:

  • Réagissez aux nouvelles données de manière séquentielle et isolée.
  • Simplifiez le multithreading.
  • Évitez l’enfer de rappel.
  • Utilisez des opérateurs qui vous permettent de déclarer comment traiter les données à chaque étape de la chaîne de manière simple.

Pour tirer le meilleur parti de ces avantages, RxJava fournit plusieurs opérateurs utiles.

Que sont les opérateurs de filtrage RxJava?

RxJava est fourni avec plusieurs opérateurs pour traiter les flux de données. Vous utiliserez Opérateurs de filtrage RxJava largement tout au long de votre carrière RxJava. Ces opérateurs de filtrage appliquent des contraintes conditionnelles aux événements que vous recevez sur votre flux de données.


Diagramme en marbre montrant la fonctionnalité de filtrage basée sur un prédicat qui filtre uniquement les valeurs paires

Vous pouvez appliquer ces contraintes pour limiter les valeurs émises par votre flux, en vous assurant de travailler uniquement avec les éléments dont vous avez besoin. Par exemple, l’image ci-dessus montre que vous pouvez utiliser un filter pour obtenir uniquement des valeurs paires de votre flux.

Les opérateurs de filtrage les plus courants sont:

  • debounce
  • distinct
  • elementAt
  • filter
  • ignoreElements
  • last
  • skip
  • skipLast
  • take
  • takeLast
  • takeUntil

Bien que vous ne travailliez qu’avec une poignée de ces opérateurs dans ce didacticiel, vous pouvez utiliser ce que vous apprenez ici pour implémenter les autres opérateurs de filtrage. Le premier opérateur que vous utiliserez est take.

Utilisation de l’opérateur Take

le take l’opérateur n’émet que le premier n éléments émis et se termine.


Diagramme en marbre pour l'opérateur de prise

Vous utiliserez cet opérateur pour prendre et afficher les restaurants les mieux notés. Ouvert RestaurantViewModel.kt et remplacer getTopRestaurants() avec:

fun getTopRestaurants() {
  _restaurantsLiveData.value = Resource.Loading

  restaurantSource
      .take(5)
      .showResults()
}

Ici, vous obtenez le flux de restaurants, ne laissez passer que les cinq premières valeurs émises, puis affichez les résultats. Regardez en bas de RestaurantViewModel.kt. Tu verras showResults():

//1
private fun Observable.showResults() {
  this.toList() //2
      .subscribeOn(Schedulers.io()) //3
      .map { Resource.Success(it) } //4
      .subscribe(_restaurantsLiveData::postValue) //5
      .addTo(disposables) //6
}

Cette fonction d’extension pratique vous aide à garder votre code propre et SECou ne vous répétez pas. Voici une ventilation détaillée:

  1. Vous créez une fonction d’extension pour afficher les résultats de tout Observable émettant Restaurant valeurs.
  2. Aussi tôt que le Observable terminé, vous créez une liste avec toutes les valeurs émises.
  3. Ensuite, vous vous abonnez au planificateur correspondant.
  4. Vous enveloppez la liste résultante dans un Resource objet avec un type de Success.
  5. Ensuite, vous envoyez tous les Resource valeurs aux valeurs correspondantes LiveData.
  6. Enfin, vous ajoutez le jetable résultant à un CompositeDisposable qui est éliminé lorsque le ViewModel est retiré. Cela évite les fuites de mémoire. N’oubliez pas de toujours nettoyer après vous. :]

Remarque: Dans RestaurantViewModel.kt vous trouverez deux observables qui vous serviront de source de données. Le premier, restaurantsSource, est le résultat direct de la requête vers votre base de données Room. Le deuxième, restaurantSource, est le précédent observable mais ses éléments sont aplatis afin que vous puissiez travailler directement avec chaque élément pour vous assurer que le flux se termine.

Construisez et exécutez. Accédez au menu de la barre d’outils et appuyez sur le Top 5 des notes article. Vous verrez maintenant la liste filtrée affichant les restaurants les mieux notés.


Écran affichant uniquement les 5 meilleurs restaurants

Les variantes Take Operator

Il existe de nombreuses surcharges et variations de take vous pouvez choisir pour mieux répondre à vos besoins:

  • takeLast: N’émet que la finale n éléments émis par un Observable. Cet opérateur a besoin d’un Observable qui se termine pour savoir quels sont les derniers éléments. Sinon, il ne les émettra pas.
  • takeUntil: Émet des éléments jusqu’à un secondaire Observable se déclenche en émettant un élément ou en se terminant.
  • takeWhile: Émet des éléments lorsqu’une condition spécifiée est vraie.
  • elementAt: Ce n’est pas nécessairement un descendant direct du take opérateur. Mais, en spécifiant un index, vous pouvez prendre cet élément particulier dans l’index correspondant, obtenant un comportement très similaire.

Ensuite, vous travaillerez avec le skipwhile opérateur.

Utilisation de l’opérateur SkipWhile

Ensuite, vous filtrerez votre liste de restaurants pour n’afficher que les restaurants classés trois étoiles ou moins. Vous pouvez commencer à réduire votre liste restreinte de restaurants possibles la prochaine fois que vous sortez. :]

le skipWhile L’opérateur vous permet d’ignorer des valeurs lorsqu’une certaine condition est vraie. Lorsque la condition n’est plus remplie, le Observable commence à émettre le reste des valeurs.


Diagramme en marbre pour l'opérateur skipWhile

Dans RestaurantViewModel.kt, remplacer getLowestRatedRestaurants() avec:

fun getLowestRatedRestaurants() {
  _restaurantsLiveData.value = Resource.Loading

  restaurantSource
      .skipWhile { it.rating > 3 }
      .showResults()
}

Vous obtenez le flux de valeurs du restaurant, puis ignorez toutes les valeurs lorsque la note est supérieure à trois.

Construisez et exécutez. Accédez au menu de la barre d’outils, mais cette fois, appuyez sur le bouton Classé 3 et moins article. Maintenant, vous verrez une liste de tous les restaurants avec une note de trois étoiles et moins. Au lieu de prendre ou d’ignorer un nombre spécifique d’éléments, vous filtrez pour une condition particulière.


Écran montrant les restaurants dont la note est inférieure à 3 étoiles

Maintenant que vous préférez filtrer vos données, il est temps d’aller plus loin.

Les variations de l’opérateur SkipWhile

skipWhile a également quelques variantes, chacune avec ses propres conditions:

  • skip: Saute le premier n puis émet le reste.
  • skipLast: Cet opérateur n’émettra que s’il dispose d’un Observable qui termine et lui fait savoir quels sont les derniers éléments. Il saute la finale n éléments émis par un Observable.
  • skipUntil: Saute les éléments jusqu’au secondaire Observable déclenche en émettant un élément ou en se terminant.

Parfois, vous ne vous souciez des valeurs émises que lorsque le flux de données est terminé. le ignoreElements L’opérateur est le candidat idéal pour ces occasions.

Utilisation de l’opérateur IgnoreElements

ignoreElements transforme le Observable dans un Completable. UNE Completable se comporte comme un Observable sauf qu’il n’émet que des événements terminaux, soit onError ou onCompleteet aucune valeur.


Diagramme en marbre montrant la fonctionnalité de l'opérateur ignoreElements

Vous utiliserez ignoreElements pour indiquer à l’utilisateur quand les données ont été récupérées et filtrées avec succès. Dans RestaurantViewModel.kt, ajoutez le code suivant au bas de showResults:

this.ignoreElements() //1
    .subscribeOn(Schedulers.io()) //2
    .observeOn(AndroidSchedulers.mainThread()) //3
    .subscribe {
      _uiLiveData.value = Resource.Success(Unit) //4
    }
    .addTo(disposables) //5

Voici une ventilation du code:

  1. Tout Observable l’utilisation de cette extension déclenche également une Completable.
  2. Le complétable s’abonne à l’ordonnanceur correspondant.
  3. Ensuite, le complétable observe le flux de données sur le thread principal fourni par RxAndroid.
  4. Chaque fois que le flux de données se termine, le complétable envoie un Resource de type Success à l’activité à travers le correspondant LiveData.
  5. Enfin, le complétable ajoute le jetable résultant à un CompositeDisposable qui dispose lorsque le ViewModel est retiré.

Votre extension ressemblera à ceci:

private fun Observable.showResults() {
  this.toList()
      .subscribeOn(Schedulers.io())
      .map { Resource.Success(it) }
      .subscribe(_restaurantsLiveData::postValue)
      .addTo(disposables)

  this.ignoreElements()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe {
        _uiLiveData.value = Resource.Success(Unit)
      }
      .addTo(disposables)
}

Étant donné que votre getTopRestaurants() et getLowestRatedRestaurants() utilisez cette extension pour afficher les résultats, votre activité est notifiée sur un autre LiveData lorsque ces flux se sont terminés avec succès.

Construisez et exécutez. Accédez au menu de la barre d’outils et appuyez sur Top 5 des notes ou Classé 3 et moins. Un toast vous avertira dès la fin du traitement des données.


Écran montrant Toast après avoir récupéré et filtré les données

Ensuite, vous découvrirez les filter opérateur.

Utilisation de l’opérateur de filtre

Le bien nommé filter est l’un des opérateurs de filtrage les plus importants et les plus polyvalents. Il ne transmet que les valeurs qui correspondent à un prédicat particulier que vous déclarez.


Diagramme en marbre montrant la fonctionnalité d'opérateur de filtre basée sur un prédicat qui filtre les valeurs qui contiennent une chaîne spécifique

le filter L’opérateur est parfait pour raccourcir votre liste aux seuls restaurants qui correspondent à votre requête de recherche spécifique. Ouvrez votre RestaurantViewModel.kt et remplacer setQueryListener() avec:

//1
fun setQueryListener(queryObservable: Observable) {
  queryObservable
      .observeOn(AndroidSchedulers.mainThread())
      .map(::filterSource) //2
      .subscribe()
}


private fun filterSource(query: String) {
  Log.d(this::class.java.simpleName, "Search query: $query")

  _restaurantsLiveData.value = Resource.Loading

  restaurantSource //3
      .filter { restaurant ->
        if (query.isEmpty()) return@filter true //4
        restaurant.name.contains(query, true)//5
      }
      .showResults()
}

Voici une ventilation:

  1. Les changements dans la requête de recherche proviennent de la queryObservable.
  2. A chaque changement de requête, vous envoyez les valeurs émises à filterSource(), une fonction d’assistance qui filtrera les valeurs.
  3. Cette fonction reçoit la nouvelle requête avec laquelle vous filtrez votre source de vérité en renvoyant true ou false.
  4. Si la requête est vide, elle ne filtre pas. En d’autres termes, il émet toutes les valeurs.
  5. Lorsque la requête n’est pas vide, elle compare chaque nom de restaurant à la requête. Si la requête fait partie du nom du restaurant, elle émet la valeur. Si le nom ne l’est pas, il ignore ce nom.

Construisez et exécutez. Appuyez sur le chercher icône, entrée Isl et remarquez que votre liste est filtrée avec chaque lettre que vous tapez.

Avec l’opérateur de filtrage, vous pouvez exploiter chaque élément de votre flux et séquentiellement et simplement le filtrer avec la logique conditionnelle que vous jugez appropriée.


Écran affichant la liste filtrée par requête

Ensuite, vous découvrirez les rebondir opérateur.

Utilisation de l’opérateur anti-rebond

Regarder votre Logcat et le filtrer pour afficher les journaux de RestaurantViewModel. Notice how the filtering occurs with every letter you enter.


Écran affichant le Logcat avec chaque entrée pour la requête de recherche

Si vous utilisiez un serveur distant, cela aurait un impact significatif sur les performances de votre application. Cela pourrait augmenter les coûts de votre serveur pour les transactions inutiles. Idéalement, vous n’appelerez votre source de données que lorsque vous êtes relativement sûr que la requête que vous avez reçue est celle que l’utilisateur souhaite rechercher.

Ce problème semble être un gros mal de tête, mais RxJava a le dos avec debounce.

le debounce L’opérateur émet uniquement un élément Observable si un temps particulier s’est écoulé sans émettre un autre élément.


Diagramme en marbre montrant la fonctionnalité de l'opérateur anti-rebond

Ce n’est pas aussi déroutant que cela puisse paraître. debounce ne laisse passer que le dernier élément dans une certaine période de temps. Ouvert RestaurantViewModel.kt et remplacer setQueryListener() avec:

fun setQueryListener(queryObservable: Observable) {
  queryObservable
      .debounce(300, TimeUnit.MILLISECONDS)
      .observeOn(AndroidSchedulers.mainThread())
      .map(::filterSource)
      .subscribe()
}

Tout ce que vous avez fait, c’est d’ajouter le debounce opérateur. Ici, debounce ne laisse passer les valeurs qu’après une pause de 300 millisecondes. Du point de vue de l’application, cela signifie que le Observable attend pour émettre le dernier élément, qui sera la requête la plus récente, jusqu’à ce que l’utilisateur arrête de taper des caractères pendant au moins 300 millisecondes.

En conséquence, vous obtenez une amélioration considérable des performances avec très peu de code.

Construisez et exécutez. Encore une fois, recherchez Isl et essayez d’écrire sans vous arrêter beaucoup entre les caractères. Dans votre Logcat, vous verrez le Observable laisser passer et filtrer une seule requête comme prévu, Isl.

Sans l’aide de RxJava, l’ajout de cette fonctionnalité d’amélioration des performances sera fastidieux.

Écran affichant Logcat avec une seule requête de recherche

Écran montrant la liste filtrée par requête

Remarque: Il existe une famille d’opérateurs similaires qui sont également pratiques dans des situations comme celle-ci, throttle. le throttle L’opérateur n’émet qu’un seul élément d’un groupe de valeurs émises dans des intervalles de temps périodiques. Voici deux pour référence:

  • throttleFirst: Émet le premier élément dans des intervalles de temps périodiques.
  • throttleLast: Émet le dernier élément dans des intervalles de temps périodiques.

Il est temps pour le dernier opérateur, distinctUntilChanged.

Utilisation de l’opérateur DistinctUntilChanged

Vous utiliserez le dernier opérateur pour résoudre un petit cas de bord. Lorsque vous recherchez une requête spécifique et ajoutez et supprimez rapidement une lettre, vous pourriez être assez rapide pour battre votre debounce opérateur. Si vous le faites, vous allez rechercher et filtrer votre liste en fonction de la même requête de recherche.

Construisez et exécutez, puis recherchez Isl. Dès que les filtres de liste continuent, ajoutez un une et supprimez-le dans votre période de rebond de 300 millisecondes.

Vérifier votre Logcat et voir les filtres de liste basés sur la même requête de recherche. Étant donné que cette requête a déjà filtré la liste, cette recherche est inutile.

Écran montrant Logcat avec les mêmes requêtes de recherche séquentielle

Écran affichant la liste filtrée par requête

C’est une solution facile avec le distintctUntilChanged opérateur. Cet opérateur ne laisse passer que des valeurs différentes de leurs prédécesseurs. Tous les éléments dupliqués séquentiels sont supprimés.

Diagramme en marbre montrant la fonctionnalité de l'opérateur distintcUntilChanged

Ouvert RestaurantViewModel.kt et remplacer setQueryListener avec:

fun setQueryListener(queryObservable: Observable) {
  queryObservable
      .debounce(300, TimeUnit.MILLISECONDS)
      .distinctUntilChanged()
      .observeOn(AndroidSchedulers.mainThread())
      .map(::filterSource)
      .subscribe()
}

Ici, vous avez ajouté le distintUntilChanged opérateur. Construire et exécuter, rechercher Isl puis ajoutez et supprimez la lettre une dans la période de rebond de 300 millisecondes. Vérifiez-vous Logcat et vous verrez que vos données n’ont été récupérées et filtrées qu’une seule fois avec la requête Isl!

Écran affichant Logcat avec une seule requête de recherche

Écran affichant la liste filtrée par requête

Remarque: Il y a un plus général distinct opérateur. Au lieu de supprimer les doublons séquentiels, comme distinctUntilChanged le fait, il supprime les doublons n’importe où sur votre flux de données.

Où aller en partant d’ici?

Vous pouvez télécharger la version finale de ce projet en utilisant le Télécharger les documents en haut ou en bas de ce didacticiel.

Félicitations pour avoir réussi tout au long! Vous pouvez désormais travailler avec des opérateurs de filtrage et comprendre leur plein potentiel. Il existe de nombreuses façons d’obtenir le même résultat. Le chemin que vous empruntez dépend de ce dont vous avez besoin et de la façon dont vous composez vos opérateurs.

Si vous souhaitez continuer votre voyage à RxJava, consultez le Opérateurs de filtrage ReactiveX Documentation. Une autre excellente ressource est notre livre génial, Programmation réactive avec Kotlin.

Si vous avez des questions, des commentaires ou si vous souhaitez montrer quels autres opérateurs de filtrage vous avez utilisés dans ce projet, n’hésitez pas à rejoindre la discussion ci-dessous!

Close Menu