Skip to content

How to Code MVP in Android

  • This is a simplified MVP structure. In reality, you can inject more lifecycle handling.
  • This example is just for illustrating the MVP pattern. In modern Android development, MVP is rarely used, so we won’t go into more advanced details here.
  • If you combine it with AutoService, MVP can become slightly more convenient — but generally, you might as well use MVVM directly.

MVP with Kotlin

kotlin
interface IContract {
    interface IViewRender {
        fun callback(message: String)
    }

    interface IPresenter {
        fun fetchData()
        
        // To Bind the lifecycle Access Process Interface
        fun attachView(viewRender: IContract.IViewRender)
        
        fun detachView()
    }
}

class View : IContract.IViewRender {
    private val presenter: Presenter = Presenter()
    
    override fun onViewCreated() {
        presenter.attachView(this)
        presenter.fetchData()
    }

    override fun onDestroy() {
        // Cancel any network requests in Presenter
        presenter.detachView()
    }

    override fun callback(message: String) {
        mBinding.tvInfo.text = message
    }
}

// Presenter
class Presenter : IContract.IPresenter {    

    private var viewRender: IContract.IViewRender? = null
    
    private val mCompositeDisposable = CompositeDisposable()

    override fun fetchData() {
        Single.create<String> { emitter ->
            Thread.sleep(2000L) // Simulate a time-consuming operation
            emitter.onSuccess("Hello World")
        }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object: SingleObserver<String> {
            override fun onSubscribe(disposable: Disposable) {
                mCompositeDisposable.add(disposable)
            }
            
            override fun onSuccess(result: String) {
                viewRender?.callback(result)
            }

            override fun onError(e: Throwable) = Unit
        })
    }

    override fun attachView(viewRender: IContract.IViewRender) {
        this.viewRender = viewRender
    }
        
    override fun detachView() {
        mCompositeDisposable.clear()
        viewRender = null
    }
}

Explanation: Here, we use the Activity/Fragment lifecycle to cancel network requests during destruction. This prevents callbacks from trying to update UI on a destroyed view, avoiding potential NullPointerException.

Just something casual. Hope you like it.