When and How to Use RxJava Disposable



Disposable

RxJava 2 introduced the concept of Disposables .

Disposables are streams/links/connections between Observer and Observable. They’re meant to give power to the Observers to control Observables. In most cases, the Observer is a UI element, ie; Fragment, Activity, or a corresponding viewmodel of those elements. While Observable represents a data flow where data can be passed from one source to another and get modified along the way if needed.

Sometimes that data flow can be long-running api requests, or can be listening to the updates in the database, and by the time you get a response or a value gets emitted - the screen that made a request might already be closed. In this case, you might end up with a memory leak, or even a crash - depending on your implementation.

So in order to avoid these issues, it is important that ui elements which are aware of Android Lifecycles, dispose their data flows when they’re not needed anymore, even if no response was emitted yet. That’s where Disposable comes handy.

Disposable itself is an interface that comes with dispose() and isDisposed() functions. In RxJava 1 there was a Subscription interface that had the same functionality. RxJava 3 introduced additional operations .

This is how to declare and assign a disposable within a Fragment:

 1   private lateinit var disposable: Disposable
 2
 3   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 4       super.onViewCreated(view, savedInstanceState)
 5
 6       disposable = api.getPosts()
 7           .observeOn(AndroidSchedulers.mainThread())
 8           .subscribe({ posts ->
 9               println(posts)
10           }, { throwable ->
11               println(throwable)
12           })
13   }

And below is the sample code to dispose the observable when the fragment’s view is destroyed; therefore, there is no subscriber listening to the changes:

1   override fun onDestroyView() {
2      if(this::disposable.isInitialized && !disposable.isDisposed) {
3          disposable.dispose()
4      }
5       super.onDestroyView()
6   }

CompositeDisposable

As an app evolves and grows, it may require getting data from multiple sources. For example, Instagram Feed screen has the endpoint to get the list of the posts and another endpoint to get the list of stories(you can notice how these two parts get updated asynchronously and don’t depend on each other 😉). The list of additional data flows can be big or small.

Instead of declaring and initializing each disposable for those data flows, RxJava 2 introduced a CompositeDisposable class to bundle multiple Disposables that can be disposed all at once. It’s basically the class that holds a collection of disposables and also offers O(1) time complexity for add(Disposable), remove(Disposable), delete(Disposable) operations.

Let’s continue with the example above, and add getStories() call:

 1   private val compositeDisposable by lazy {
 2       CompositeDisposable()
 3   }
 4
 5   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 6       super.onViewCreated(view, savedInstanceState)
 7
 8       compositeDisposable.add(
 9           api.getPosts()
10               .observeOn(AndroidSchedulers.mainThread())
11               .subscribe({ posts ->
12                   println(posts)
13               }, { throwable ->
14                   println(throwable)
15               })
16       )
17
18       compositeDisposable.add(
19           api.getStories()
20               .observeOn(AndroidSchedulers.mainThread())
21               .subscribe({ stories ->
22                   println(stories)
23               }, { throwable ->
24                   println(throwable)
25               })
26       )
27   }

Upon onDestroyView() the CompositeDisposable gets cleared:

1   override fun onDestroyView() {
2       compositeDisposable.clear()
3       super.onDestroyView()
4   }

Unlike Disposable, CompositeDisposable has two ways of disposing subscriptions:

  1. dispose()
  2. clear()

Both operations dispose the previously contained subscriptions, but dispose() operation assigns true to the disposed variable, which later does not let you add, remove,or delete any other disposables.

If you have CompositeDisposable in fragments, then you should call clear() upon onDestroyView() because the fragment doesn’t get destroyed. It goes to the backstack, therefore you don’t need to create a new instance of CompositeDisposable every time when a fragment’s view is re-created.

If you have CompositeDisposable in activities, then call dispose() upon onDestroy().

SerialDisposable

While CompositeDisposable is quite popular due to its convenience, there is the lesser known SerialDisposable class, that RxJava 2 introduced. SerialDisposable is a disposable container that allows atomically updating/replacing the contained Disposable with another Disposable, disposing the old one when updating, and handling the disposition when the container itself is disposed.

Or simply, whenever you want to replace the existing subscription with the new one - use SerialDisposable.

For example, Instagram has a search functionality where their api returns results for each search query. A user first typed in fru in EditText, saw an empty screen, and then finished typing the whole word fruits. Since there could be cases of unstable internet connections which can’t guarantee the order of network request/responses, in our use case, the first response came back for fruits, then the second response was for fru. Because the second response was the last response - ui renders the results of fru, which is confusing for the user since they now see more broader results than the specific fruits results.

In order to prevent these confusions, it’s better to use set() operation of SerialDisposable, which sets the latest disposable whenever the request happened and disposes the previous one:

 1   private val serialDisposable by lazy {
 2       SerialDisposable()
 3   }
 4
 5   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 6       super.onViewCreated(view, savedInstanceState)
 7
 8       view.editText.doAfterTextChanged { text ->
 9           serialDisposable.set(
10               api.searchFor(text)
11                   .observeOn(AndroidSchedulers.mainThread())
12                   .subscribe({ result ->
13                       println(result)
14                   }, { throwable ->
15                       println(throwable)
16                   })
17           )
18       }
19   }

Operation replace() on the other hand, does not dispose the previous disposable, which means the result of it will be emitted upon subscribe().

Think of SeriaDisposable as a manual version of switchMap() operator, that you can use to glue your ui callbacks and reactive data flows.

Conclusion

In conclusion, ui elements can subscribe to observables and decide when to listen to its changes and when not to. Whenever the ui element gets removed from the content view, it’s important to dispose all of its subscriptions, otherwise they will cause a memory leak.

RxJava provides Disposable to manage the subscriptions. It also has CompositeDisposable and SerialDisposable classes that implement Disposable.

CompositeDisposable is a container that can hold multiple disposables and dispose them all at once.

SerialDisposable is a container that is used to replace the current disposable with the new one.


I hope this post was helpful. If you have questions or something is unclear - please leave a comment!


❤️ Was this post helpful?

If you liked the article, give a shoutout to @aida_isay on Instagram, Threads, or Twitter and help share this article.

Thank you for your support 🙌