Thuy's Blog

A convenient RxJava utility for ViewModels: autoClear()

June 26, 2018

Speaking of ViewModels in the Architecture Components, the onCleared() callback can be a perfect place to do some sort of termination works, for example, de-registration of listeners, or disposing RxJava Subscriptions. And in some cases, you dispose a Subscription just to stop observing a never-complete Observable (or a never-complete Flowable):

class PokemonsViewModel(pokemonService: PokemonService) : ViewModel() {
  private var subscription: Disposable? = null

  init {
    subscription = pokemonService
        .pokemons
        .subscribe { println(it) }
  }

  override fun onCleared() {
    subscription?.dispose()
  }
}

Or in some other cases, you like to cancel an ongoing RxJava operations, such as, loading data from database or fetching data from network:

class PokemonsViewModel(private val pokemonService: PokemonService) : ViewModel() {
  private var subscription: Disposable? = null

  fun loadNearestPokemons() {
    subscription = pokemonService
        .findNearestPokemons() // Returns `Single<List<Pokemon>>`.
        .subscribe(Consumer { println(it) })
  }

  override fun onCleared() {
    subscription?.dispose()
  }
}

But in certain circumstances, your ViewModel may scale up and there may be multiple Subscriptions inside. For instance, imagine when I combine the two previous examples? In that case, CompositeDisposable will come to rescue like below:

class PokemonsViewModel(private val pokemonService: PokemonService) : ViewModel() {
  private val subscriptions: CompositeDisposable = CompositeDisposable()

  init {
    subscriptions.add(
        pokemonService
            .pokemons
            .subscribe { println(it) }
    )
  }

  fun loadNearestPokemons() {
    subscriptions.add(
        pokemonService
            .findNearestPokemons() // Returns `Single<List<Pokemon>>`.
            .subscribe(Consumer { println(it) })
    )
  }

  override fun onCleared() {
    subscriptions.dispose()
  }
}

You can also simplify a little bit more with the plusAssign operator from RxKotlin:

-subscriptions.add(
+subscriptions +=
    pokemonService
        .pokemons
        .subscribe { println(it) }
-)

However, that isn’t the selling point of this blog-post. If inside your ViewModel, imagine you can refactor into an autoClear() extension function like below:

  protected fun Disposable.autoClear() {
    subscriptions += this
  }

Then the previous usages of adding subscriptions will be like:

  init {
    pokemonService
        .pokemons
        .subscribe { println(it) }
        .autoClear()
  }

  fun loadNearestPokemons() {
    pokemonService
        .findNearestPokemons() // Returns `Single<List<Pokemon>>`.
        .subscribe(Consumer { println(it) })
        .autoClear()
  }

To me, that looks easier, and simpler to use but one important benefit is that, if you extract the logic related to managing subscriptions into a sort of base class ViewModel, let’s say like following ReactiveViewModel, then the subscriptions will be well encapsulated:

abstract class ReactiveViewModel : ViewModel() {
  private val subscriptions: CompositeDisposable by lazy { CompositeDisposable() }

  protected fun Disposable.autoClear() {
    subscriptions += this
  }

  public override fun onCleared() {
    subscriptions.dispose()
  }
}

What it means is, all subclasses of ReactiveViewModel won’t be able to access subscriptions to misuse it. For example, when the subscriptions is exposed, some may call dispose() at a different point rather than onCleared(), thus modifying the purpose of subscriptions.

So PokemonsViewModel will become a little more compact now:

class PokemonsViewModel(private val pokemonService: PokemonService) : ReactiveViewModel() {
  init {
    pokemonService
        .pokemons
        .subscribe { println(it) }
        .autoClear()
  }

  fun loadNearestPokemons() {
    pokemonService
        .findNearestPokemons() // Returns `Single<List<Pokemon>>`.
        .subscribe(Consumer { println(it) })
        .autoClear()
  }
}

Hope it’s useful.


Thuy Trinh

Written by Thuy Trinh who lives and works in Frankfurt, Germany building robust Android apps. You should follow him on Twitter