淺談Android中的MVP模式

MVP這種設計模式在Android領域越來越火,經常會聽到某某Android應用採用了MVP+Retrofit+RxJava的架構,甚至很多工程師都說面試過的人喜歡提MVP,卻並不知道它真正的機制。那麼,究竟什麼是MVP模式,它又與MVC模式有什麼異同呢?


MVP與MVC

相信對於大部分開發者而言,MVC是一種非常熟悉的模式,它廣泛的應用於web等諸多常見的軟件中。MVC將整個工程分爲三個部分,Model(模型)、View(視圖)、Controller(控制器)中,對於以往常用的Android項目而言,更多的人們會將佈局文件作爲View,而將Activity(或Fragment,下同)作爲Controller。

然而,在Activity中實際上做了很多的事情,不僅處理了業務邏輯,有時也會對UI進行操作,這樣就使得Activity看起來既像Controller層又像View層,隨着項目的不斷髮展,這些Activity和Fragment就會變得越來越臃腫,難以擴展和維護,因此便出現了MVP模式。

作爲MVC的演化版本,在MVP模式中,將整個工程分爲了Model(模型)、View(視圖)、Presenter(表示器),與MVC的異同見下圖(來自圖片):


在圖中我們能夠看出,MVP與MVC最大的不同就是在於Model與View並不能直接交互,而是隔着一層Presenter,相比於MVC,MVP中的Model會更少的關注業務邏輯,與View的耦合度也進一步降低,這種模式不僅使得代碼邏輯更加清晰,同時也能減少我們部署以及單元測試的時間。


Demo

網上很多例子都是使用的用戶登錄(當然也有很多比較複雜的),所以實現了一個其他的功能,主要用於說明各模塊之間的關係。

demo實現的效果如下圖,主要功能爲輸入一個字符串,點擊按鈕將它變成大寫並且輸出


在這個demo中的java代碼結構如下圖:


  • Activity用於展示內容
  • biz是業務邏輯層,用於避免在Model中進行邏輯處理
  • model是模型層
  • presenter是表示器層,用於處理Model和View之間的交互
  • view中封裝了一些接口,和activity共同組成了視圖層

(1)Model層

在Model層中,我們定義了一個Model,名爲MyModel

package com.example.steveyg.androiddemo.model;

/**
 * Created by steveyg on 2016/12/20.
 */

public class MyModel {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}
一般很少有實體只包含一個屬性,不過此處用於舉例就寫了一個簡單的實體類。接着定義了相應的biz

package com.example.steveyg.androiddemo.biz;

/**
 * Created by steveyg on 2016/12/20.
 */

public interface IMyBiz {
    public void exec(String str, OnExecListener listener);
}
package com.example.steveyg.androiddemo.biz;

import com.example.steveyg.androiddemo.model.MyModel;

/**
 * Created by steveyg on 2016/12/20.
 */

public interface OnExecListener {
    void execSuccess(MyModel model);
}

package com.example.steveyg.androiddemo.biz;

import com.example.steveyg.androiddemo.model.MyModel;

/**
 * Created by steveyg on 2016/12/20.
 */

public class MyBiz implements IMyBiz {
    @Override
    public void exec(final String str, final OnExecListener listener) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                MyModel model = new MyModel();
                String result = str.toUpperCase();
                model.setStr(result);
                listener.execSuccess(model);

            }
        }.run();
    }
}

在實現IMyBiz接口的時候,採用了異步的操作,因爲大部分需要處理數據的時候都是要和數據庫或服務器交互的。

(2)View層

在View層中,首先定義了一個佈局文件,裏面有一個提示語,一個輸入框和一個按鈕:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.example.steveyg.androiddemo.activity.MainActivity">

    <TextView
        android:layout_alignTop="@+id/edit"
        android:layout_alignBottom="@+id/edit"
        android:gravity="center"
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:text="TEXT : " />

    <EditText
        android:id="@+id/edit"
        android:layout_marginRight="10dp"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/submit"
        android:text="submit"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/edit"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>
接着,我們定義一個接口IMyView,裏面定義了視圖層需要進行的操作:

package com.example.steveyg.androiddemo.view;

import com.example.steveyg.androiddemo.model.MyModel;

/**
 * Created by steveyg on 2016/12/20.
 */

public interface IMyView {
    public String getStr();

    public void showSuccess(MyModel model);
}
getStr()用來獲取輸入框中的文字,showSuccess()用於展示最後的結果。接着,在Activity中實現相應的方法

package com.example.steveyg.androiddemo.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.example.steveyg.androiddemo.R;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.presenter.MyPresenter;
import com.example.steveyg.androiddemo.view.IMyView;

public class MainActivity extends AppCompatActivity implements IMyView {

    private EditText mEditText;
    private Button mButton;
    private MyPresenter mPresenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    @Override
    public String getStr() {
        return mEditText.getText().toString();
    }

    @Override
    public void showSuccess(MyModel model) {
        Toast.makeText(this,model.getStr(),Toast.LENGTH_SHORT).show();
    }

    private void init(){
        mPresenter = new MyPresenter(this);
        mEditText = (EditText)findViewById(R.id.edit);
        mButton = (Button)findViewById(R.id.submit);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.exec();
            }
        });
    }
}
(3)Presenter層
Presenter層主要是讓View和Model進行交互,因此我們定義一個執行相應功能的方法:

package com.example.steveyg.androiddemo.presenter;

import android.os.Handler;

import com.example.steveyg.androiddemo.biz.IMyBiz;
import com.example.steveyg.androiddemo.biz.MyBiz;
import com.example.steveyg.androiddemo.biz.OnExecListener;
import com.example.steveyg.androiddemo.model.MyModel;
import com.example.steveyg.androiddemo.view.IMyView;


/**
 * Created by steveyg on 2016/12/20.
 */

public class MyPresenter {
    private IMyBiz myBiz;
    private IMyView myView;
    private Handler mHandler = new Handler();

    public MyPresenter( IMyView view) {
        this.myBiz = new MyBiz();
        this.myView = view;
    }

    public void exec() {
        myBiz.exec(myView.getStr(), new OnExecListener() {
            @Override
            public void execSuccess(final MyModel model) {
                //通過Handler在UI線程中處理
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        myView.showSuccess(model);
                    }
                });

            }
        });
    }
}

至此,一個簡單的MVP demo就完成啦,不難看出,相比於MVC模式,代碼量增加了很多,但是邏輯更加清晰,在實際的項目中,還是要好好設計下整個項目的結構,而且選定一種模式最好能夠堅持的使用下去,在中途切換模式,有可能會導致整個項目的重構。


參考資料

http://blog.csdn.net/lmj623565791/article/details/46596109

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0227/2503.html

http://www.cnblogs.com/liuling/archive/2015/12/23/mvp-pattern-android.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章