Android日记之MVC、MVP和MVVM

作者: 居居居居居居x | 来源:发表于2019-11-17 00:23 被阅读0次

    前言

    在项目的设计模式中,听到最多的就是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使用上会复杂很多,学习成本会很大。

    参考

    相关文章

      网友评论

        本文标题:Android日记之MVC、MVP和MVVM

        本文链接:https://www.haomeiwen.com/subject/mohyictx.html