Reactive Programming 102
Overview
- Usage examples
- Pitfalls
- Operator of the week
How we use Rx
Autosuggest
autosuggestInput
.throttleLast(250, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(input -> {
return autosuggestManager.autosuggest(input);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread)
.subscribe(results -> ...);

Recent Searches & Price Alerts
Observable.zip(
// Get recent searches
recentSearchStore.getRecentSearches(),
// Get Price Alerts
priceAlertStore.getPriceAlerts(),
// Get latest saved search configuration
searchConfigStore.latestSearchConfig(),
(searches, alerts, lastConfig) -> {
// Batch the data based on common City
...
})
.flatMap(batched -> {
// Get images for the cities
return Observable.zip(
Observable.just(batched),
imageStore.getCityImage(batched.cityId()),
(ars, s) -> ars.setImageUrl(s));
});

User-vector upload
userVector
// Avoid uploading too often
.throttleLast(3, TimeUnit.SECONDS)
// Upload
.flatMap(it -> userVectorStore.upload(it))
// Re-subscribe on failure
.retry()
// Upload on background thread
.subscribeOn(Schedulers.newThread())
// Don't care about the results
.subscribe();
+
- Boilerplate code--
- Code quality++
- Active Rx community
- Common, de-facto pattern for:
- Composing tasks
- Cancelling tasks
- Handling task errors
- Handling threading
-
- Learning curve is steeper than it seems
- Debugging difficulties++
- Cargo-cult coding is really dangerous with Rx
Pitfalls
Observable.create()
- You need to handle everything -> gotta be careful
- No backpressure-support
- No automatic unsubscription-support
- If possible, use .just() / .from() / .defer() instead
Observable.defer(() -> Observable.just(expensiveCalculation()))
.subscribe(System.out::println);Unsubscription
- Only best-effort!
- After you unsubscription some events might pop in!
- Depends on the setup of the stream
- The operators used
- The threading environment
- It's often not an issue - but be on the lookout
Error blocks
- You can skip the error handling block on subscription
Observable.range(0, 10)
.doOnNext(integer -> {
if (integer.equals(9)) throw new RuntimeException("Surprise!");
})
.subscribe(System.out::println); // -> exploding at 9 :(- You can easily skip it even when it's needed
- Tip: Use full Subscribers instead of Actions only
- Check out Jake's video on this
Endless streams
- 0...n onNext() -> onComplete() | onError()
- Endless steams do heave their place
- E.g. touch input, sensor input, etc.
- Keep in mind that certain operators go crazy with them
- E.g. concatMap()

Unsafe usage
- RxJava is heavily optimized for multi-threaded usage
- Unsafe is being used
- RxJava uses lock-free structures from JCTools
- E.g. MPSC and SPSC arrays of various flavors
- Unsafe provides CAS operations on primitive types
- Samsung - Android 5.0, 5.0.1, 5.0.2 -> crash :(
- We can switch it off: If Samsung -> No Unsafe
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP &&
"samsung".equalsIgnoreCase(Build.MANUFACTURER)) {
System.setProperty("rx.unsafe-disable", "True");
}Operators
- RxJava can result in really short, concise code
- It's not free though -> complexity
- Don't just assume, test!
- Unit test, unit test, unit test
- Especially for more complex streams
Lambda hell
- Look out for readability
- For complex streams, you might not want to inline everything
- Especially without lambdas! (< Java 1.8)
- What may help here:
- >= Java 1.8
- Retrolambda
- Kotlin
Operator of the week:
*Map()
map()
- Convert A -> B
- Converts via a simple function

B convert(A input){ ... }- Synchronous
- Keeps ordering
flatMap()
- Convert A -> B
- Converts via an Observable!

Observable<B> convert(A input){ ... }- Sync OR async depending on the converting Observable
- 'Flat' -> flattens out the results of the converting Observable to the main stream
- You don't care about the ordering of the output events!
concatMap()
- Convert A -> B
- Converts via an Observable!
Observable<B> convert(A input){ ... }- Sync OR async depending on the converting Observable
- Inner observables must complete
- The ordering of output events follows the ordering of input events

switchMap()
- Convert A -> B
- Converts via an Observable!
Observable<B> convert(A input){ ... }- Sync OR async depending on the converting Observable
- Keeps 'ordering': Only outputs the events of the most recent inner Observable!

The end!
Reactive Programming I.
By rzsombor
Reactive Programming I.
- 716