Là où le code rencontre la clarté

RxJava Observable : Cas d’usage, Opérateurs et Bonnes Pratiques

R

Dans l’univers de la programmation réactive avec RxJava, Observable est sans doute la classe la plus utilisée. Que ce soit pour écouter des événements utilisateur, exécuter des appels réseau ou manipuler des listes de données, l’Observable est au cœur des pipelines réactifs.

Dans cet article, on plonge en profondeur dans le fonctionnement de Observable, ses cas d’usage typiques, ses opérateurs les plus puissants, et les bonnes pratiques pour l’utiliser efficacement.

Qu’est-ce qu’un Observable ?

Un Observable est un flux de données qui peut émettre zéro, une ou plusieurs valeurs dans le temps, de manière asynchrone ou synchrone. Il notifie un Observer à chaque nouvel élément, à l’erreur éventuelle, ou à la fin du flux (onNext, onError, onComplete). Par exemple,

Observable<String> obs = Observable.just("Bonjour", "RxJava");
obs.subscribe(System.out::println);

Cas d’usage courants de Observable

Gestion d’événements UI : Dans une application Android ou JavaFX, tu peux écouter les clics d’un bouton, les frappes clavier ou les gestes de l’utilisateur via Observable.

Appels réseau ou base de données : Tu peux enchaîner un appel HTTP et le traiter comme un flux de données.

Transformation de listes : RxJava permet de transformer des collections de manière fluide avec des opérateurs comme map, filter, ou flatMap.

Timers, intervalles et tâches périodiques : Tu peux créer un timer réactif qui émet à intervalles réguliers :

Observable.interval(1, TimeUnit.SECONDS)
    .subscribe(i -> System.out.println("Tick: " + i));

Les opérateurs essentiels de Observable

RxJava met à disposition des dizaines d’opérateurs pour transformer, filtrer, combiner et contrôler les flux.

map() : Transforme chaque élément du flux.

Observable.just(1, 2, 3)
    .map(i -> i * 2)
    .subscribe(System.out::println); // 2, 4, 6

filter() : Filtre les éléments selon une condition.

Observable.just(1, 2, 3, 4)
    .filter(i -> i % 2 == 0)
    .subscribe(System.out::println); // 2, 4

flatMap() : Transforme un élément en un autre Observable, puis fusionne tout.

Observable.just("a", "b")
    .flatMap(s -> Observable.just(s.toUpperCase(), s + "!"))
    .subscribe(System.out::println);

concat() : enchaîne plusieurs Observable l’un après l’autre (ordre garanti, un à la fois).

Observable<String> source1 = Observable.just("A", "B");
Observable<String> source2 = Observable.just("C", "D", "E");

Observable.concat(source1, source2)
    .subscribe(System.out::println); // A, B, C, D, E

concat()

attend que le premier flux se termine avant de passer au suivant.

merge() : fusionne les émissions en parallèle sans garantie d’ordre.

Observable<String> source1 = Observable.interval(100, TimeUnit.MILLISECONDS)
    .map(i -> "S1-" + i)
    .take(3);

Observable<String> source2 = Observable.interval(50, TimeUnit.MILLISECONDS)
    .map(i -> "S2-" + i)
    .take(3);

Observable.merge(source1, source2)
    .subscribe(System.out::println); // S2-0, S1-0, S2-1, S1-1, S2-2, S1-2

merge()

utilise-le quand tu veux écouter plusieurs sources en temps réel.

zip() : combine les éléments de manière paire (1er avec 1er, 2e avec 2e…).

Observable<String> source1 = Observable.just("A", "B", "C");
Observable<Integer> source2 = Observable.just(1, 2, 3, 4, 5);

Observable.zip(source1, source2, (letter, number) -> letter + number)
    .subscribe(System.out::println); // A1, B2, C3

zip()

s’arrête à la fin du flux le plus court.

combineLatest() : combine les dernières valeurs émises par chaque flux dès qu’un des deux émet.

Observable<String> source1 = Observable.interval(100, TimeUnit.MILLISECONDS)
    .map(i -> "L" + i)
    .take(3);

Observable<String> source2 = Observable.interval(150, TimeUnit.MILLISECONDS)
    .map(i -> "R" + i)
    .take(3);

Observable.combineLatest(source1, source2, (l, r) -> l + "+" + r)
    .subscribe(System.out::println); // L1+R0, L2+R0, L2+R1, L2+R2

combineLatest()

très utile pour afficher une vue combinée de deux sources asynchrones.

doOnNext, doOnComplete, doOnError : Pour insérer des actions (logs, side effects) sans perturber le flux.

Bonnes pratiques pour utiliser Observable

Toujours se désabonner : Une subscription mal gérée peut entraîner des fuites mémoire. Utilise un CompositeDisposable ou un AutoDispose pour gérer automatiquement la durée de vie du flux.

CompositeDisposable disposables = new CompositeDisposable();
disposables.add(observable.subscribe());

Utilise subscribeOn et observeOn à bon escient

  • subscribeOn définit le thread d’exécution du producteur.
  • observeOn définit le thread du consommateur (ex. : UI).
observable
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe();

Gère les erreurs proprement : Ne laisse jamais une erreur non traitée. Utilise les opérateurs comme onErrorReturn, retry, ou onErrorResumeNext.

Évite les chaînes trop longues : Un pipeline de 15 opérateurs est difficile à maintenir. Garde ton code lisible, et décompose les transformations en méthodes claires.

Commence simple : N’utilise Flowable ou les opérateurs avancés que si nécessaire. Observable suffit dans la majorité des cas. N’hésite pas consulter cet article Observable vs Flowable : Quand utiliser l’un ou l’autre ? pour en apprendre davantage.

Conclusion

Observable est le poinçonneur du réactif : simple, élégant, et capable de gérer des flux de données variés. Bien utilisé, il permet d’écrire un code asynchrone, modulaire et maintenable. Connaître les bons opérateurs et adopter les bonnes pratiques te donnera un énorme avantage dans le développement d’applications modernes basées sur RxJava.

À propos de l'auteur

Konrad Ahodan

Développeur Java, passionné de l'architecture logicielle et la création de solutions durables. Je partage mes idées sur Kaflash pour aider la communauté tech à progresser.

Par Konrad Ahodan
Là où le code rencontre la clarté