Analysis and Practice of MVP Design Model

Posted by Pigmaster on Sat, 25 May 2019 23:31:47 +0200

This article introduces the concept of MVP in Android development and practical MVP examples.

Introduction of MVP Model

  • View corresponds to Activity and is responsible for drawing View and interacting with users.
  • Model is still the business logic and entity model.
  • Presenter is responsible for completing the interaction between View and Model

Legend, in fact, is very simple. Presenter has the task of connecting View and Model, so that our Activity will not deal with the business-related cumbersome code.
So the key to MVP is how to let Presenter layer complete the interaction between View and Model.

Next, we will study the design and coding of MVP pattern from specific practical cases.

The case is simple, and we have implemented a list:

  1. Display Toast of simulated loading before loading data.
  2. The simulation method subthread loads the data.
  3. Loading data completes hiding loading and displays the data in the list.

Project catalog structure

The structure is very clear. I have abstracted a base class from both the View layer and the Presenter layer to reduce the coupling of our code.

View level

BaseView
public interface BaseView {
    void showLoading();
    void hideLoading();
}

There are only two commonly used methods of trying to operate. You can add more View operations here, such as showToast.

GirlView
public interface GirlView extends BaseView{
    void setListItem(List<GirlEntity> data);
    void showMessage(String message);
}

It inherits BaseView, not only holds the view operation method in BaseView, but also adds the view method of its own list page, displays the list data, and clicks Item to display the Message information.

Presenter

BasePresenter
public class BasePresenter<T> {

    public T mView;

    public void attachView(T view){
        this.mView = view;
    }

    public void dettach(){
        mView = null;
    }
}

Here we use a generic T in the base class of Presenter, because we don't know which View is held in the specific Presenter layer, so we use generics to solve the problem of holding View in BasePresenter. At the same time, we provide two methods in BasePresenter, attacheView (View v) is used to bind Presenter and View, dettach () is used to unbind the relationship between Presenter and View layer, so that we can achieve effective resource release and avoid OOM.

GirlPresenter
public class GirlPresenter extends BasePresenter<GirlView>{

    private Handler mHandler;

    public GirlPresenter() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    public void getGirls(){
        mView.showLoading();
        final List<GirlEntity> list = new ArrayList<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3*1000);
                    for (int i=0; i < 10; i++){
                        GirlEntity girlEntity = new GirlEntity();
                        girlEntity.name = "Zhao Liying"+i;
                        girlEntity.imgRes = R.drawable.img;
                        list.add(girlEntity);
                    }

                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mView.hideLoading();
                            mView.setListItem(list);
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

Here I inherit BasePresenter and specify that GirlPresenter holds GirlView. Then I implemented the operation of getting and binding data to View in GirlPresenter, which implements the interaction between Model layer and View layer. Actually, the code is not complicated. Before I start loading the data, I use View layer to display loading. Then I process the business data in the sub-thread. After processing the data, I switch to the main thread to finish hiding loading and data display in View layer.

Activity

Activity layer is mainly used for the implementation of View layer method and the initialization of Presenter layer method.

Here I also abstract a BaseActivity.

BaseActivity
public abstract class BaseActivity<V,T extends BasePresenter<V>> extends AppCompatActivity{

    public T presenter;

    /**
     * Realize your own P-tier according to different pages
     * @return
     */
    public abstract T createPresenter();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        presenter = createPresenter();
        //Association
        presenter.attachView((V)this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.dettach();
    }
}

Similarly, I defined a Presenter in BaseActivity with generics, because we were not sure which Presenter was needed in a specific activity. Because Presenter holds the generic object of View, a two-tier generic approach is needed here. Then I cleverly realized the initialization of concrete Presenter in concrete activity by a static method named createPresenter (). Then I associate Presenter and View's processing relationship with Activity's life cycle, which perfectly implements binding and unbinding operations.

GirActivity

Next is our specific operation in an Activity.

public class GirlActivity extends BaseActivity<GirlView,GirlPresenter> implements GirlView{

    private ListView mListView;

    @Override
    public GirlPresenter createPresenter() {
        return new GirlPresenter();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);

        presenter.getGirls();
    }

    @Override
    public void showLoading() {
        Toast.makeText(this,"Loading data...",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void hideLoading() {
        Toast.makeText(this,"Data loading completed...",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void setListItem(List<GirlEntity> data) {
        GirlAdapter adapter = new GirlAdapter(data,this);
        mListView.setAdapter(adapter);
    }

    @Override
    public void showMessage(String message) {

    }
}

We can see that the code in Activity is very simple and clear. We initialize GirlPresenter so that GirlPresenter and GirlView are associated. By calling the getGirls() method in GirlPresenter, we can get the list data and call back the operation of View to the GirlView method implemented by our Activity.

At this point, the use of MVP is over. Maybe some abstractions of base classes are involved in the code, which makes our understanding of MVP patterns a little complicated. But abstraction is to simplify our code and reduce coupling. I believe that this work must be done. Therefore, it is suggested that you do it yourself. You can simply instantiate a particular Presenter without abstraction, so that a particular Presenter can hold a specific View, which is more convenient for you to understand. Of course, it is recommended that you Abstract View and Presenter according to your understanding and needs, and then abstract Activity, so that MVP pattern can be perfectly applied to our project.

If it is helpful to you, you are welcome to leave a message for discussion and pay attention to it.

Topics: Android