RxJava

Real-World RxJava Examples

Explore practical examples of using RxJava in real-world scenarios. These examples demonstrate how to solve common challenges using reactive programming patterns.

Network Requests with Error Handling

This example shows how to handle network requests with RxJava, including proper error handling, timeouts, retries, and thread management.

Key Concepts Demonstrated

  • Moving network operations to background threads with subscribeOn
  • Implementing timeouts to prevent waiting indefinitely
  • Automatic retries for transient failures
  • Fallback values with onErrorReturn
  • Thread switching for UI updates with observeOn
  • Proper loading state management
1import io.reactivex.rxjava3.core.Observable;
2import io.reactivex.rxjava3.schedulers.Schedulers;
3import java.util.concurrent.TimeUnit;
4
5// Simulate a network request with timeout, retry, and error handling
6public Observable<String> fetchDataFromApi(String endpoint) {
7 return Observable
8 .fromCallable(() -> {
9 // Simulate network call
10 System.out.println("Making request to: " + endpoint);
11 if (Math.random() < 0.3) { // 30% chance of failure
12 throw new IOException("Network error");
13 }
14 Thread.sleep(2000); // Simulate network delay
15 return "Response from " + endpoint;
16 })
17 .subscribeOn(Schedulers.io()) // Perform on IO thread
18 .timeout(5, TimeUnit.SECONDS) // Timeout after 5 seconds
19 .retry(2) // Retry up to 2 times
20 .onErrorReturn(error -> "Error fetching data: " + error.getMessage());
21}
22
23// Usage
24fetchDataFromApi("/api/users")
25 .observeOn(AndroidSchedulers.mainThread()) // Switch to main thread for Android UI
26 .doOnSubscribe(d -> showLoadingIndicator())
27 .doFinally(() -> hideLoadingIndicator())
28 .subscribe(
29 response -> displayData(response),
30 error -> showError(error)
31 );

When to Use This Pattern

This pattern is ideal for any network-dependent operation in your application, such as API calls, file downloads, or any remote data fetch that needs to be resilient against failures and should not block the main thread.

Real-time Form Validation

This example demonstrates how to implement real-time form validation that enables/disables a submit button based on input validity.

Key Concepts Demonstrated

  • Reactive UI updates based on user input
  • Combining multiple validation streams with combineLatest
  • Preventing duplicate emissions with distinctUntilChanged
  • Declarative UI state management
1// Android example with RxBinding
2// Validates a form when both email and password meet criteria
3Observable<Boolean> emailValid = RxTextView
4 .textChanges(emailEditText)
5 .map(CharSequence::toString)
6 .map(email -> email.contains("@") && email.length() > 5);
7
8Observable<Boolean> passwordValid = RxTextView
9 .textChanges(passwordEditText)
10 .map(CharSequence::toString)
11 .map(password -> password.length() >= 8);
12
13Observable.combineLatest(emailValid, passwordValid,
14 (isEmailValid, isPasswordValid) -> isEmailValid && isPasswordValid)
15 .distinctUntilChanged()
16 .subscribe(isValid -> {
17 submitButton.setEnabled(isValid);
18 if (isValid) {
19 submitButton.setAlpha(1.0f);
20 } else {
21 submitButton.setAlpha(0.5f);
22 }
23 });

When to Use This Pattern

Use this pattern when you need to perform real-time validation of user input across multiple fields with interdependencies. It's perfect for login forms, registration flows, or any multi-step input process where you want to provide immediate feedback.

Reactive Search Implementation

This example shows how to implement a responsive search feature that queries an API as the user types, with proper debouncing and cancelation of outdated requests.

Key Concepts Demonstrated

  • Debouncing rapid user input with debounce
  • Cancelling outdated requests with switchMap
  • Filtering out short queries that would return too many results
  • Showing loading states during queries
  • Handling errors gracefully
1// Search example with debounce and switchMap
2Observable<String> searchQueryObservable = RxTextView
3 .textChanges(searchEditText)
4 .map(CharSequence::toString)
5 .debounce(300, TimeUnit.MILLISECONDS) // Wait for 300ms of inactivity
6 .distinctUntilChanged() // Only emit when the query changes
7 .filter(query -> query.length() >= 2); // Only search for queries with 2+ chars
8
9searchQueryObservable
10 .doOnNext(query -> showLoading())
11 .switchMap(query -> searchApi.search(query)
12 .subscribeOn(Schedulers.io())
13 .onErrorReturn(error -> Collections.emptyList())
14 )
15 .observeOn(AndroidSchedulers.mainThread())
16 .subscribe(
17 results -> {
18 hideLoading();
19 displayResults(results);
20 },
21 error -> {
22 hideLoading();
23 showError(error);
24 }
25 );

When to Use This Pattern

This pattern is ideal for implementing search features, autocomplete, typeahead suggestions, or any UI that needs to react to user input by fetching data from an external source. It provides a smooth user experience by avoiding unnecessary API calls and ensuring that only the most recent search results are displayed.

More Real-World Examples

Data Synchronization

Learn how to implement efficient data synchronization between local cache and remote servers using RxJava's powerful operators.

View Example

Event Throttling

Discover how to handle high-frequency events like scroll, resize, or mousemove with proper throttling to maintain performance.

View Example

Caching Strategies

Explore different caching strategies with RxJava to balance freshness and performance in your data layer.

View Example

Parallel Processing

Learn how to efficiently process large datasets in parallel while controlling thread usage and handling backpressure.

View Example