Skip to content

Binder usage and lifecycle

After 1.2.4, Binder is located in a separate module. You may need to add dependency on com.github.badoo.mvicore:binder to have it available.

What is the Binder and why is it good for me?

If you wrote your first Feature, now you may wonder how to start using it.

Do you subscribe to its state directly? Do you call .accept(wish) on it manually?

Well, you can, but there are better ways to do that, which also come with some bonuses.

Remember when in the Core concepts we said that Feature is a Consumer of Wish and an ObservableSource of State? And that in general, the framework is working with outputs of type ObservableSource<T> and inputs of type Consumer<T>?

The Binder is a tool that can:

  • automatically connect those outputs to those inputs by a subscription using a super simple syntax
  • dispose of this subscription when its lifecycle expires
  • automatically add Middlewares around all inputs (logging and time travel debugging, or your custom one)

Binder creation

Creating an instance is as simple as:

val binder = Binder()

with manual disposal, or

val binder = Binder(lifecycle)

for automatic disposal of the created bindings when lifecycle expires (more on that below).

Binding reactive endpoints

You can connect outputs and inputs directly if they are of the same type:

val output: ObservableSource<A> = TODO()
val input: Consumer<A> = TODO()

binder.bind(output to input)

Or using a transformer if they are of different types:

val output: ObservableSource<A> = TODO()
val input: Consumer<B> = TODO()
val transformer: (A) -> B? = TODO()

binder.bind(output to input using transformer)

Note

You can return null from transformer if the element emitted by an output should not reach the associated input. Binder will guarantee that null values are not forwarded in the stream.

Lifecycle handling

Since all connections created by the Binder are rx subscriptions under the hood, disposing needs to be taken care of.

At the simplest:

val binder = Binder()

// bind stuff
binder.bind(a to b)
binder.bind(c to d)

// don't forget to call later
binder.dispose()

But you don't need to do this manually. Binder can take an instance of Lifecycle in its constructor, which is really only a way to signal termination:

interface Lifecycle : ObservableSource<Lifecycle.Event> {

    enum class Event {
        BEGIN,  // currently not used
        END     // signals termination
    }
}

A Lifecycle instance can be created by mapping any other observable stream:

val stream: Observable<T> = TODO()
val lifecycle = Lifecycle.wrap(stream.map { Lifecycle.Event.END })

Or if you are on Android and using the mvicore-android dependency, you can leverage the AndroidBinderLifecycle class to automatically create Binder lifecycle from Android lifecycle:

val lifecycle = AndroidBinderLifecycle(activity) // or any other Android LifecycleOwner

In both the above cases you don't need to worry about disposing: whenever Lifecycle signals it, the Binder instance will dispose of the created subscriptions:

val binder = Binder(lifecycle)

// bind stuff
binder.bind(a to b)
binder.bind(c to d)

// no need to dispose manually, will be handled automatically