Android开发Android开发Android技术知识

Android日记之MVC、MVP和MVVM

2019-11-17  本文已影响0人  居居居居居居x

前言

在项目的设计模式中,听到最多的就是MVC、MVP和MVVM这3个架构的设计模式了,也是经常面试会问到的设计模式,本篇文章将会通过一个小demo来进行代码实战来简单的讲解这3个设计模式的区别。

demo功能分析及编写

图1

我将通过3种设计模式来实现此demo的编写,demo的功能是用户输入一个账户名称,然后通过查询按钮就可以查询到用户的等级信息。我们这时候可以分析一下需求。


需求图,来源见参考,侵删

然后我们开始正式编写,首先建立一个账号的实体类和请求结果的回调接口,并且编写下布局。

//账号的实体类
public class Account {
    
    //名字
    private String name;
    //账号等级
    private int level;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }
}

//请求结果的回调接口
public interface ICallback {

    void onSuccess(Account account);

    void onFailed();
}
<!--布局-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


    <Button
        android:id="@+id/btn_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询" />

    <TextView
        android:id="@+id/txt_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="test"
        android:textSize="20dp" />

</LinearLayout>

接着我们就开始正式编写了,在对应的Activity下输入以下代码。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private TextView textView;
    private EditText editText;
  

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        initView();
    }


    private void initView() {
        editText = findViewById(R.id.edit_text);
        textView = findViewById(R.id.txt_text);
        
    
        findViewById(R.id.btn_text).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {

        getAccountData(getUser(), new ICallback() {
            @Override
            public void onSuccess(Account account) {
                showSuccessPage(account);
            }

            @Override
            public void onFailed() {
                showErrorPage();
            }
        });
    }


    //获取用户输入的信息
    private String getUser(){
        return editText.getText().toString();
    }

    //展示获取信息成功界面
    private void showSuccessPage(Account account){
        textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel());
    }
    
    //展示获取信息失败界面
    private void showErrorPage(){
        textView.setText("获取数据失败");
    }
    
    //查询用户数据,模拟请求数据
    public void getAccountData(String accountName, ICallback callback){
        Random random = new Random();
        boolean isSuccess = random.nextBoolean();

        if (isSuccess){
            Account account = new Account();
            account.setName(accountName);
            account.setLevel(100);
            callback.onSuccess(account);
        }else {
            callback.onFailed();
        }
    }
}

从这段代码我们可以看出,如果不使用设计模式编写的话,发现Activity是又充当展示界面和业务逻辑,这样Activity会显的很臃肿,这时候我们就需要进行解耦了,也就是需要用到设计模式的时候了。

MVC的使用

MVC就是Model-View-Controller的缩写,即模型-视图-控制器。在Android中对应的话,可以看如图。


对应如图,来源见参考,侵删

这就是一个MVC的比较常见的模型图,箭头代表事件的传递方向,这里注意一下,Model如果传递给View,我们一般不会让Model持有View的引用,我们可以使用注册监听来实现。说了这么多,我们接下来来优化一下代码。


MVC各自负责的东西,来源见参考,侵删
刚刚说明了下MVC,在通过这个图,其实我们发现主要就是把查询账号数据的业务逻辑给分到了Model层,接着我们就创建一个Model类,把这个逻辑放进去。
public class MVCModel {

    //模拟请求数据
    public void getAccountData(String accountName, ICallback callback){
        Random random = new Random();
        boolean isSuccess = random.nextBoolean();

        if (isSuccess){
            Account account = new Account();
            account.setName(accountName);
            account.setLevel(100);
            callback.onSuccess(account);
        }else {
            callback.onFailed();
        }
    }
}

放到这里后怎么使用呢,其实很简单,我们直接让View层持有Model层的引用就可以使用这个逻辑了。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private TextView textView;
    private EditText editText;
    //让View层持有model层的引用
    private MVCModel mvcModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //实例化
        mvcModel = new MVCModel();
    }


    private void initView() {
        editText = findViewById(R.id.edit_text);
        textView = findViewById(R.id.txt_text);

        findViewById(R.id.btn_text).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {

        mvcModel.getAccountData(getUser(), new ICallback() {
            @Override
            public void onSuccess(Account account) {
                showSuccessPage(account);
            }

            @Override
            public void onFailed() {
                showErrorPage();
            }
        });
    }


    //获取用户输入的信息
    private String getUser(){
        return editText.getText().toString();
    }

    //展示获取信息成功界面
    private void showSuccessPage(Account account){
        textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel());
    }
    //展示获取信息失败界面
    private void showErrorPage(){
        textView.setText("获取数据失败");
    }
}
MVC结果如图
但其实这样也是有缺点的,虽然这一定程度上实现了Model和View的分离,降低了代码的耦合性,但是其实在Android中,Activity充当了Controller和View的责任,就是Activity即是控制器,又要充当部分view层的工作,难以完全解耦,并且随着项目的提上Controller会更加的臃肿,这样子的话模型图就会变成这个样子。
模型图,来源见参考,侵删

MVP的使用

MVP就是Model-View-Presenter,在MVP中,是可以进行解耦分离的,因为在MVP中Model和View是不能直接通信的,它们只能通过Presenter来进行通信,这样子的话,Activity的功能就会被大幅度简化,不再充当控制器,主要做view层的工作就好了。


MVP模型图,来源见参考,侵删

接着我们开始写代码,首先我们这里View层的那些展示功能主要以接口的方式进行实现,Model层主要提供查询数据方面的功能,Presenter层主要负责业务逻辑的处理。


MVP各层功能,来源见参考,侵删
首先我们编写View层的接口。
public interface IMVPView {

    //获取用户输入的信息
    String getUser();

    //展示获取信息成功界面
    void showSuccessPage(Account account);
    
    //示获取信息失败界面
    void showErrorPage();
}

然后我们编写Presenter层和Model层的业务逻辑,Model的业务逻辑跟MVC是一样的。

//Model层的业务逻辑
public class MVPModel {

    //模拟请求数据
    public void getAccountData(String accountName, ICallback callback){
        Random random = new Random();
        boolean isSuccess = random.nextBoolean();

        if (isSuccess){
            Account account = new Account();
            account.setName(accountName);
            account.setLevel(100);
            callback.onSuccess(account);
        }else {
            callback.onFailed();
        }
    }
}

//Presenter层
public class MVPPresenter  {

    //持有View接口的引用
    private IMVPView mView;
    //持有Model层的引用
    private MVPModel mModel;

    //通过构造函数获得实现View层接口的Activity
    public MVPPresenter(IMVPView mView) {
        this.mView = mView;
        mModel = new MVPModel();
    }
    
    //具体的业务逻辑
    public void getData(String accountName) {
        mModel.getAccountData(accountName, new ICallback() {
            @Override
            public void onSuccess(Account account) {
                mView.showSuccessPage(account);
            }

            @Override
            public void onFailed() {
                mView.showErrorPage();
            }
        });
    }
}

刚刚也说过了View层和Model进行交互就需要通过Presenter来进行交互,就可以让Presenter层获得View层和Model的引用来进行交互,然后在写具体的业务逻辑就好啦,最后我们在View层实现View层的接口,然后通过让View层持有Presenter的引用传递进去就行,最后通过getData()方法来获取最终的结果。

//View层的具体实现
public class MainActivity extends AppCompatActivity implements View.OnClickListener, IMVPView {

    private TextView textView;
    private EditText editText;
    private MVPPresenter mvpPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        //View层传递给Presenter
        mvpPresenter = new MVPPresenter(this);
    }


    private void initView() {
        editText = findViewById(R.id.edit_text);
        textView = findViewById(R.id.txt_text);

        findViewById(R.id.btn_text).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        mvpPresenter.getData(getUser());
    }

    @Override
    public String getUser() {
        return editText.getText().toString();
    }

    @Override
    public void showSuccessPage(Account account) {
        textView.setText("用户账号:"+ account.getName()+ "用户等级:"+ account.getLevel());
    }

    @Override
    public void showErrorPage() {
        textView.setText("获取数据失败");
    }
}
MVP结果
MVP的有点就很明显了,每个层的职责划分明显,更加易于维护,缺点也很明显,因为View主要是通过接口实现,如果项目复杂的话,接口是非常多的,而且Presenter的职责会越来越臃肿,使用MVP的建议就是接口一定要规范化,或者使用第三方插件来自动生成MVP代码。还有就是根据项目的复杂程度,部分简单的功能就没必要使用MVP模式来进行设计。

MVVM的使用

MVVM是全名Model-View-ViewModel的缩写,它跟MVP是很相似的,区别就在于Presenter替换成了ViewModel,它是在MVP的基础上实现了,数据视图的绑定(DataBinding),当数据变化时,视图也会自动更新,反之,当视图变化是,数据也会更新,优点就是减少了接口,也告别了繁琐的findViewById操作。


MVVM模型,来源见参考,侵删

使用MVVM之前,我们就需要了解DataBinding的基本用法,DataBinding是谷歌推出的实现数据绑定的框架(数据与视图的双向绑定),它可以更好的帮助我们在Android中实现MVVM。要使用DataBinding,首先我们在Build文件下输入以下代码就可以使用了。

android {
    compileSdkVersion 28


    defaultConfig {
        applicationId "com.ju.mvvmdemo"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    //输入这段代码
    dataBinding {
        enabled = true
    }

}

然后我们需要将布局修改为DataBinding布局。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

       
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />


        <Button
            android:id="@+id/btn_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="查询" />

        <TextView
            android:id="@+id/txt_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20dp" />

    </LinearLayout>
</layout>

然后我们就可以在data标签里面输入实体类了,我们把Account实体类输入进去。

<data>
    <variable
        name="viewModel"
        type="com.ju.mvvmdemo.MVVMViewModel" />
</data>

接着我们在Activity里通过DataBindingUtil.setContentView()去绑定视图,这里注意以下,ActivityMainBinding是在build的时候自动生成的对应绑定视图,生成的名字就是你绑定的layout的名字。

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    }

}

然后我们创建Model层的逻辑,业务还是和之前MVP、MVC是一样的。

public class MVVMModel {

    //模拟请求数据
    public void getAccountData(String accountName, ICallback callback){
        Random random = new Random();
        boolean isSuccess = random.nextBoolean();

        if (isSuccess){
            Account account = new Account();
            account.setName(accountName);
            account.setLevel(100);
            callback.onSuccess(account);
        }else {
            callback.onFailed();
        }
    }
}

接下来就是重点了,怎么双向绑定数据和视图呢,其实我们在layout对应的控件输入格式@{******}就可以了,我们这样输入。

<EditText
    android:text="@={viewModel.userInput}"
    android:id="@+id/edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />


<Button
    android:id="@+id/btn_text"
    android:onClick="@{viewModel.getData}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="查询" />

<TextView
    android:id="@+id/txt_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{viewModel.result}"
    android:textSize="20dp" />

刚刚我们在data标签里面放入了viewModel,然后就可以在控件里面通过你想要的方法来实时更新view了,如果是view实时更新来改变数据的话(比如EditText),在@后面加一个 = 就好了。这里我们也设置了一个点击事件,当点击这个按钮后,会调用getData()方法,这里修改完毕后我们还需要Account实体类,我们让他继承BaseObservable,然后在你需要实时更新的变量设置相应的注解。

public class Account extends BaseObservable {

    private String name;
    private int level;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //设置注解
    @Bindable
    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
        //通过这段代码刷新视图
        notifyPropertyChanged(BR.level);
    }
}

然后就是重点了,接下来编写ViewModel层的代码。

public class MVVMViewModel extends BaseObservable {

    private MVVMModel mvvmModel;
    private String result;
    private String userInput;


    //一般传入Application对象,方便在ViewModel中使用Application
    //比如sharedpreferences需要使用
    public MVVMViewModel(Application application) {
        mvvmModel = new MVVMModel();
    }

    @Bindable
    public String getUserInput() {
        return userInput;
    }

    public void setUserInput(String userInput) {
        this.userInput = userInput;
        notifyPropertyChanged(BR.userInput);
    }

    @Bindable
    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
        notifyPropertyChanged(BR.result);
    }

    //点击按钮后调用这个方法,参数要View
    public void getData(View view) {

        mvvmModel.getAccountData(userInput, new ICallback() {
            @Override
            public void onSuccess(Account account) {
                String info = account.getName() + "|" + account.getLevel();
                setResult(info);
            }

            @Override
            public void onFailed() {
                setResult("更新失败");
            }
        });
    }
}

最后我们在Activity中和ViewModel绑定,就可以进行使用了。

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        //实例化MVVMModel
        MVVMViewModel mvvmViewModel = new MVVMViewModel(getApplication());
        //绑定ViewModel
        binding.setViewModel(mvvmViewModel);
    }
}
MVVM运行结果
MVVM优点也很明显,因为数据和视图进行了双向的绑定,这样就可以极大的简化了代码,缺点就是相较于MVP和MVC使用上会复杂很多,学习成本会很大。

参考

上一篇下一篇

猜你喜欢

热点阅读