During my last job interview I was asked about the MVP pattern.

Why this question?

It does makes plenty of sense to ask this particular question during a job interview. If you do not know any viable alternative to MVVM, then how do you know why you are using the MVVM pattern in the first place?

Please note I am not very good at implementing the MVP. The MVVM and LiveData are my thing. Still, the MVP may be useful when you do need to invoke some code directly on the view. The presenter doesn’t need to know about the view, though. You only need to pass to it the actual lambdas you are going to invoke.

I wrote the example project presented in this article as a refactoring of another project to prove that - at least to some extent - I understand the difference between the MVP and the MVVM.

The project

The project used in this article is MVP. It is a refactoring of MVVM.

Please read the previous article to understand the original architecture.

As I was refactoring the project, I changed the package name from pl.org.seva.mvvm, deleted the package viewmodel and created a new package presenter. In the new version of the project I do not use the Android’s class ViewModel nor LiveData at all.

The presenter

This is the code of the presenter that subscribes to the RxJava’s Observable described in the previous article, interacts directly with the view and handles the lifecycle:

class ActivityPresenter(
        private val desc: (String) -> Unit,
        private val conf: (Int) -> Unit) {

    fun present(act: ActivityDesc) {
        desc(act.desc)
        conf(act.conf)
    }

    @Suppress("unused")
    class Builder(private val owner: LifecycleOwner) {
        lateinit var presentDesc: (String) -> Unit
        lateinit var presentConf: (Int) -> Unit

        fun build() {
            val presenter = ActivityPresenter(presentDesc, presentConf)
            val lifecycle = owner.lifecycle
            var last: ActivityDesc? = null

            val d = ar.observable
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe { act ->
                        if (lifecycle.currentState == Lifecycle.State.RESUMED) {
                            presenter.present(act)
                            last = null
                        }
                        else {
                            last = act
                        }
                    }
            lifecycle.addObserver(object : LifecycleObserver {
                @OnLifecycleEvent(value = Lifecycle.Event.ON_RESUME)
                fun presentLast() {
                    last?.let { presenter.present(it) }
                }

                @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY)
                fun dispose() {
                    d.dispose()
                }
            })
        }
    }

    companion object {
        fun build(owner: LifecycleOwner, block: Builder.() -> Unit) = Builder(owner).apply(block).build()
    }
}

The presenter really only needs to keep references to lambdas that are a part of the view. It doesn’t need to know more about the view:

class ActivityPresenter(
        private val desc: (String) -> Unit,
        private val conf: (Int) -> Unit)

What the build() function does is the following: it collecs the lifecycle from the LifecycleObserver, creates a Disposable by calling subscribe() on the Observable, and eventually disposes it.

The crucial line is:

.observeOn(AndroidSchedulers.mainThread())

Because the presenter deals with the view, it can only do so on Android’s main thread.

If the view happens to be paused, the above code will store the value last generated by the Observable, but it won’t present it until the view is resumed. Notice this line:

last?.let { presenter.present(it) }

I cannot replace it with:

if (last != null) {
    presenter.present(last!!)
}

The value of last is updated from another thread, so it can be changed to null even after the check if (last != null) has already been made. Calling last?let captures the most up-to-date value of last, and if it is not null then executes the block.

In the Fragment

This is what it takes to start observing activity recognition in the Fragment, using the above extension function:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    observeActivityRecognition {
        presentDesc = { activity_desc.text = it }
        presentConf = { activity_conf.text = it.toString() }
    }
}

I use the following extension function:

fun LifecycleOwner.observeActivityRecognition(block: ActivityPresenter.Builder.() -> Unit) =
        ActivityPresenter.build(this, block)

Calling build() not only creates an instance of ActivityPresenter but also starts observing the lifecycle of the receiver (here: LifecycleObserver).

Altertatively, I could implement one function build() to just create an instance of ActivityPresenter, and then write a separate function, for example observe(), to start observing the lifecycle. Creating two different functions would promote separation of concerns, but I decided to stick to only one function in order to promote a more concise code. Your particular architecture choice is up to you.

Testing

Testing is not different at all from the techique discussed in the previous article. Because testing doesn’t depend on choice of architecture (MVVM or MVP) you can use identical code.

Please note, though, that because I changed the package name, you also have to change the testInstrumentationRunner in your module-level build.gradle:

testInstrumentationRunner 'pl.org.seva.mvp.mock.MockTestRunner'

Conclusion

In this article I described the way in which you could refactor the MVVM architecture - described in the previous article - to the MVP alternative.

I do not exactly understand the advantages of one over the other, but I do not personally fancy handling the lifecycle manually, so I will stick to the MVVM, even thought I hope to have proven that switching from one to the other is trivial.