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;45// Simulate a network request with timeout, retry, and error handling6public Observable<String> fetchDataFromApi(String endpoint) {7 return Observable8 .fromCallable(() -> {9 // Simulate network call10 System.out.println("Making request to: " + endpoint);11 if (Math.random() < 0.3) { // 30% chance of failure12 throw new IOException("Network error");13 }14 Thread.sleep(2000); // Simulate network delay15 return "Response from " + endpoint;16 })17 .subscribeOn(Schedulers.io()) // Perform on IO thread18 .timeout(5, TimeUnit.SECONDS) // Timeout after 5 seconds19 .retry(2) // Retry up to 2 times20 .onErrorReturn(error -> "Error fetching data: " + error.getMessage());21}2223// Usage24fetchDataFromApi("/api/users")25 .observeOn(AndroidSchedulers.mainThread()) // Switch to main thread for Android UI26 .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 RxBinding2// Validates a form when both email and password meet criteria3Observable<Boolean> emailValid = RxTextView4 .textChanges(emailEditText)5 .map(CharSequence::toString)6 .map(email -> email.contains("@") && email.length() > 5);78Observable<Boolean> passwordValid = RxTextView9 .textChanges(passwordEditText)10 .map(CharSequence::toString)11 .map(password -> password.length() >= 8);1213Observable.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 switchMap2Observable<String> searchQueryObservable = RxTextView3 .textChanges(searchEditText)4 .map(CharSequence::toString)5 .debounce(300, TimeUnit.MILLISECONDS) // Wait for 300ms of inactivity6 .distinctUntilChanged() // Only emit when the query changes7 .filter(query -> query.length() >= 2); // Only search for queries with 2+ chars89searchQueryObservable10 .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 ExampleEvent Throttling
Discover how to handle high-frequency events like scroll, resize, or mousemove with proper throttling to maintain performance.
View ExampleCaching Strategies
Explore different caching strategies with RxJava to balance freshness and performance in your data layer.
View ExampleParallel Processing
Learn how to efficiently process large datasets in parallel while controlling thread usage and handling backpressure.
View Example