A not so well known api in RxJava is the .hide()
operator.
When does one use the hide operator in Rx?
From the docs:
Hides the identity of this Observable and its Disposable.
Allows hiding extra features such as Subject’s Observer methods or preventing certain identity-based optimizations (fusion). there are a lot of complex operations that take place internally in RxJava (like internal queue creation, worker instantiation + release, numerous atomic variables being created and modified.)
If this doesn’t make too much sense, let’s look at examples to make this clear. Consider a typical MVVM usecase:
class MyAndroidVm : ViewModel() {
private val outputSubject = BehaviorSubject.createOnDefault(initialViewState())
init {
Observable.just(service.pollModelData())
.map {
// ... do something with the Event that pipes a result
// or view state eventually
}
.subscribe { viewState ->
outputSubject.onNext(viewState)
}
}
fun listenToViewState(): BehaviorSubject<ViewState> {
return outputSubject
}
// ...
}
In this sample code, the ViewState is basically what the Activity would consume. So within the ViewModel, we pipe the data into a Subject, which is then exposed to the Activity like so:
class MyAndroidActivity: Activity {
// ...
override fun onResume() {
viewModel.listenToViewState()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { viewState ->
// bind Android Views and ViewState object
}
}
}
This is all great, however the trouble with exposing a Subject is that it allows the activity to modify the internal state of the Behavior Subject from the outside. This is non-ideal as we only want the ViewModel controlling this.
class MyAndroidActivity: Activity {
// ...
override fun onResume() {
viewModel.listenToViewState()
.onNext(badViewState()) // we want to avoid this
.observeOn(AndroidSchedulers.mainThread())
.subscribe { viewState ->
// bind Android Views and ViewState object
}
}
}
So we don’t want to expose the Subject directly. It would make more sense instead to just expose an Observable.
This is where the
.hide()
operator comes in handy. It basically converts a Subject into an Observable.
// inside the ViewModel
fun listenToViewState(): Observable<ViewState> {
outputSubject.hide()
// this returns an Observable (instead of a Subject)
}
But there’s actually more to the hide operator. It’s not as simple as merely converting a Subject -> Observable. If i merely wanted to just convert the Subject to an Observable, I could have just used the cast operator:
outputSubject.cast(ViewState::class.java) // this returns an Observable
So you may ask when should one use .hide
vs just a .cast
Operator fusion and performance
Let’s revisit the documentation for .hide
:
Allows hiding extra features such as Subject’s Observer methods or preventing certain identity-based optimizations (fusion).
This is where things get interesting. We’ve already seen the “hiding extra features such as Subject’s Observer methods” bit through our example.
But apparently, there are also some identity-based optimizations that are avoided. I wasn’t aware of what these optimizations were but RxJava savante and maintainer David Karnok has written a couple of great posts on operator fusion.
I’m not going to regurgitate his content here but the tl;dr version is - for all the thread hopping and asynchronous magic that RxJava enables, there are some internal costs to pay (obviously). When you combine a sequence of operators (which you’re bound to do for any Rx call), there are times where we incur an overhead.
Operator fusion is the idea that we can fuse or combine some of the internal operations to help reduce some of that overhead. If you want a more detailed (and accurate) description of the details, head on over to David’s blog for some of those details.
So if you don’t need some of these additional optimizations, use the .hide()
operator.
I also love looking at the source code, so i did just that for .hide
and noticed it simply wraps and sends the observable with ObservableHide
. This proces of re-creating a wrapped version of the ObservableHide
pares down a lot of the operator functionality. Reading the source also made me realize that this would work:
outputSubject.hide().hide().hide().hide().hide()
But I wouldn’t be worrying about performance and functionality at that point.