Writing in the front

The MVP architecture for Android has been around for a long time. And for Android MVP implementation mode, there is no standard implementation.

Now on the market is the most popular Google open source out of a set of MVP model, this model can go to this Google HOME MVP open source address to view.

This blog post is not about Google’s MVP model. It’s an MVP model based on my own understanding. This MVP model has the following advantages over Google’s MVP model:

  1. Multiple presenters can be bound to a single page for reuse
  2. Lifecycle distribution to presenters,
  3. View binding and unbinding to presenters automatically.
  4. Eliminate the Contract class to avoid class explosion

MVP Concept Description

  • Model: Data provider layer, responsible for providing data to presenters or providing a data processing entry
  • View: The View layer is responsible for receiving Presenter notifications and performing interface updates
  • Presenter: A View/Model hub that receives commands from the View layer and notifies the View to update its interface.

It is a little empty to layer the MVP with the above text, so here we use a simple sample code to illustrate the concept of MVP layering. Here is a simple MVP implementation of the login page:

interface LoginView:MVPView {
    fun onLoginSuccess()
    fun onLoginFailed()
}

class LoginPresenter(view:DemoView):MVPPresenter<DemoView>(view) {

    fun login(username:String, password:String) {
    	LoginApis.login(username, password, object Callback {
    		override fun onSuccess() {
    			view.onLoginSuccess()
    		}
    		
    		override fun onFailed() {the onLoginFailed ()}})}} class LoginActivity: BaseMVPActivity (), LoginView {/ / creation and binding Presenter. val presenter = LoginPresenter(this) override fun createPresenters() = arrayOf(presenter) override funonLoginSuccess() {// Receive the return data of the data request task and show easytoasts.default.show ("Login successful")
	}
	
	override fun onLoginFailed() {// Receive the return data of the data request task and show easytoasts.default.show ("Login successful")}... // Click login funonLoginClick() { val username = ... val password = ... Presenter. Login (username, password)// Initiate a login task request}}Copy the code

1. LoginView

interface LoginView:MVPView {
    fun onLoginSuccess()
    fun onLoginFailed()
}
Copy the code

Inherit and extend the MVPView interface. Many people like to refer to this class directly as the V layer in the MVP, but in fact, I prefer to call this a communication protocol interface that is provided by the V layer to the P layer for p-V binding, which is used in the P layer to notify the V layer of interface updates, similar to providing a Callback to the P layer for use

2. LoginActivity

class LoginActivity:BaseMVPActivity(),LoginView {

	override fun onLoginSuccess() {... } override funonLoginFailed() {... } // Initiate login task request funonLoginClick() {presenter.login(username, password)}
}
Copy the code

The real View layer. It can be an Activity, Fragment, etc. It’s where the actual interface updates happen.

The View layer needs to hold the Presenter’s object so that it can be used to initiate specific data requests for tasks when the Presenter is needed, such as in the example above. A presenter initiates a login task and receives a task processing callback to update the interface through LoginView

3. LoginApis

LoginApis.login(username, password, object Callback {
    override fun onSuccess() { view.onLoginSuccess() }
    
    override fun onFailed() { view.onLoginFailed() }
})
Copy the code

The Model layer, also known as the data provider layer.

Unlike other MVPS, there is no requirement that the Model layer define a special interface to implement. All functional apis. Both can be considered as Model layers. LoginApis, for example, are the web task entry used to provide the login module.

There are two modes of communication between the Model layer and Presenter layer: one is to get specified data directly from the Model layer, and the other is to get specified data through asynchronous callback, which is the communication mode of LoginApis here.

Be careful to avoid passing presenters directly to the Model layer for data retrieval. Ensure Model independence

4. LoginPresenter

Presenter layer, the intermediate hub layer that connects the V-M. Complex data business logic is processed here.

Combined with the above example: A complete M-P-V communication process can be divided into the following steps:

  1. byV to PInitiate specific processing tasks
  2. PTo receiveV layerInitiated task. callM layerAPI, get toThe original data
  3. PFrom theM layerTo get toThe original dataPreprocessing (this example does not do this because it is simpler).
  4. willAfter processingThe data. throughV layerProvides the protocol interface, notification toV layerTo update the interface.

MVP implementation

Through the above examples and explanations. I believe that you can make the MVP model have a certain understanding of the following we will introduce the entire MVP model framework step by step

1. Basic communication protocol interface definition :MVPView

interface MVPView {
    fun getHostActivity():Activity
    fun showLoadingDialog()
    fun hideLoadingDialog()
    fun toastMessage(message:String)
    fun toastMessage(resId:Int)
}
Copy the code

Some basic protocol methods are defined in MVPView. These methods are required by all V layers. Such as Toast display, Dialog display while loading for asynchronous tasks, etc.

2. Basic Presenter creation

open class MVPPresenter<T:MVPView>(private var view:T?) { fun attach(t:T) { this.view = t } fundetach() { this.view = null } fun isViewAttached() = view ! = null fun getActivity() = view? .getHostActivity()? :throw RuntimeException("Could not call getActivity if the View is not attached") // Lifecycle delegate open fun onCreate(bundle: Bundle?) {}... open funonDestroy(){}
}
Copy the code

Let’s break it down one by one:

In the first place. As mentioned in the MVP notes above, MVPView is the protocol interface provided for p-V binding, so there will be p-V binding and unbinding (for ease of use, there will be p-V binding for the default constructor).

fun attach(t:T) { this.view = t }
fun detach() { this.view = null }
Copy the code

Then, we often need to use bound activities in the P layer to perform various operations. The P layer does not recommend holding Context instances, so it provides a getActivity method to get the correct Activity instance from the bound View using:

fun getActivity() = view? .getActivity()? :throw RuntimeException("Could not call getActivity if the View is not attached")
Copy the code

The isViewAttached method is an API specifically designed for asynchronous callback tasks. Because if you are using asynchronous callbacks to retrieve data from the Model layer. It is likely that the view will be unbound and empty before the callback message is received. Notification failure

So. Generally speaking. For tasks with asynchronous callback operations, it should be at the callback. Check whether the bond is unbound. Skip current operation if unbound:

if(! isViewAttached())returnview? .hideLoadingDialog()Copy the code

Finally, a bunch of onXXX lifecycle methods are provided. Used to bind life cycles in activities/Fragments.

3. V-p connector

In contrast to other MVPS, MVPDispatcher is provided as a V-P connector:

Class MVPDispatcher{private Val Presenters :MutableList<MVPPresenter<*>> = mutableListOf() // ==== Add and remove Presenter ======== fun <V:MVPView> addPresenter(presenter:MVPPresenter<V>) {... } internal fun <V:MVPView> removePresenter(presenter:MVPPresenter<V>) {... } / / = = = = binding lifecycle = = = = = = = = = = fun dispatchOnCreate (bundle: bundle? {... }... fun dispatchOnRestoreInstanceState(savedInstanceState: Bundle?) {... }}Copy the code

You can see that the connector does the following:

  1. addPresenterwithremovePresenter: to the containerpresentersTo add or removePresenter instanceBind multiple presenters to a single page.
  2. dispatchOnXXX: Through the addedpresenterforLife cycle notification. Do the v-P life cycle binding effect
  3. On receiving layer VdestroyDestroy notifications automatically remove and unbind allPresenter instance.
fun dispatchOnDestroy() {
	presenters.forEach {
		if(it.isViewattached ()) {it.ondestroy ()} // After the life method is sent. Automatic unbinding removePresenter(it)}}Copy the code

4. Create BaseMVPActivity

Finally, the real V layer is created: MVP base class building for Activity/Fragment!

Use BaseMVPActivity as an example. The fragment base class construction is much the same.

First, we need to determine what the BaseMVPActivity needs to do:

1. A specific V layer needs to have a unique MVPDispatcher for operation

abstract class BaseMVPActivity:Activity() {
	val mvpDispatcher = MVPDispatcher()
}
Copy the code

2. Implement the default protocol using MVPView

abstract class BaseMVPActivity:Activity(), MVPView { ... override fun getHostActivity():Activity {... } override funshowLoadingDialog() {... } override funhideLoadingDialog() {... } override fun toastMessage(message:String) {... } override fun toastMessage(resId:Int) {... }}Copy the code

3. Implement V-P and one-to-many binding with MVPDispatcher

abstract class BaseMVPActivity:Activity(), MVPView { ... Subclasses provide all presenters that need to be bound to the current page. open fun createPresenters():Array<out MVPPresenter<*>>? = null override fun onCreate(savedInstanceState: Bundle?) {... // Create all presenter instances and bind createPresenters() with mvpDispatcher? .forEach { mvpDispatcher.addPresenter(it) } } }Copy the code

Take the LoginActivity example at the top. Suppose we now need to add another captcha logic to the login page. This logic is placed in the CaptchaPresenter:

class LoginActivity:BaseMVPActivity(),LoginView, CaptchaView {val loginPresenter = loginPresenter (this) // Val captchaPresenter = CaptchaPresenter(this) // Set multiple Presenter Override fun createPresenters() = arrayOf(loginPresenter, CaptchaPresenter)... }Copy the code

This enables reuse of Presenter. When you need to share some basic business logic. This one-to-many binding is a nice feature!

4. Implement v-P life cycle association management with MVPDispatcher

abstract class BaseMVPActivity:Activity(), MVPView { ... // other codes override fun onCreate(savedInstanceState: Bundle?) {... mvpDispatcher.dispatchOnCreate(intent? .extras) } ... override funonDestroy() {... // mvpDispatcher automatically unbinds all presenters when they are destroyed. / / so specific V layer does not need to be myself for a solution to the operation manual mvpDispatcher. DispatchOnDestroy ()}}Copy the code

This is what a basic V-layer base class implementation class needs to do. Here. The entire MVP infrastructure is complete!

5. MVP architecture is open source

Because of this, the MVP architecture is actually a pretty simple architecture. So I put the source code of this architecture in EasyAndroid component library.

EasyAndroid as an integrated component library, this library of integrated components, including the following features, you can rest assured to use ~~

1. Design independently

Components exist independently and do not depend on each other, and only part of the components in the library need to be integrated. It is also very convenient to copy only the corresponding component file for use

2. Lightweight design

Because it is a component integration library, the design of each component is required to be as concise and light as possible. Avoid introducing a lot of useless code for a small feature.

No component has more than 100 methods. Most components have less than 50.

As V layer base class implementation of different projects will have certain differences. Such as the Activity base class selection (AppCompatActivity/v4Activity/Activity), or MVPView display style design, etc. So true V layer base class implementations like BaseMVPActivity are not put into lib. It is provided separately in the sample project.

In need of use, by copy this part of the source code directly to the project can be used

MVP module in EasyAndroid library. Only three basic support classes MVPView, MVPPresenter and MVPDispatcher are provided. Avoid introducing useless code.

Source link

  • EasyAndroid open source component library address

https://github.com/yjfnypeu/EasyAndroid

  • Base support classesMVPView,MVPPresenter,MVPDispatcherThe source address

https://github.com/yjfnypeu/EasyAndroid/tree/master/utils/src/main/java/com/haoge/easyandroid/mvp

  • V layer base class implementationandSimple sample sample codeaddress

https://github.com/yjfnypeu/EasyAndroid/tree/master/app/src/main/java/com/haoge/sample/easyandroid/activities/mvp