01 架构介绍

先来看一下MVC、MVP、MVVM的架构图。

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

从这些架构图中,可以看到每种架构都有3个模块以及数据活动方向箭头。

模块

在体系架构中,首先要做的便是把体系全体按照必定的准则划分红模块。

数据活动

模块划分之后,模块之间的通讯,便是数据的活动。在Android中,活动数据包含两部分,事情和数据。

架构

模块和模块之间的数据通讯办法构成不同的架构。在这3种架构中,都是把体系全体划分红了3个模块:视图层,数据层,事务层。 他们之间的区别在于,模块之间的通讯办法(数据活动方向)不一致。

  • MVC是视图层接收到事情后调用到事务层处理事务逻辑,事务层调用数据层处理数据,数据层再调用视图层更新页面。
  • MVP是视图层接收到事情后调用到事务层处理,事务层调用数据层处理数据,数据层处理数据后回调给事务层,事务层再回调给视图层更新页面。(数据层已不再持有视图层,他们之间通过事务层(Presenter)交互,详细使用接口完成,使数据层和视图层解耦。
  • MVVM在MVP的基础上完成了视图层和事务层的双向数据绑定(data binding),不再通过接口的办法交互,ViewModel不在和Presenter一样持有视图层,使视图层和事务层解耦。

02 详细完成

MVC

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

视图层:在MVC架构中, Android的xml布局文件和Activity/Fragment文件被划分为View视图层。 由于xml作为视图层功用太弱,只可以完成页面的布局,不可以完成页面数据和事情的处理。需求和Activity一同才可以构成一个完好的视图层。

事务层:大多数的MVC架构开发的安卓项目, 并没有把Controller事务层独立出来,而是将事务层也在Activity/Fragment中完成。这导致了Activity/Fragment的代码非常臃肿,这便是MVC的缺陷之一。 在本例中,咱们会将事务层独立出来,完成一个规范的MVC架构。

数据层:数据层Model指的是,数据管理模块,这包含了数据的获取,处理。存储等。 MVP、MVVM的架构中的Model也是一样。后面不再赘述。

代码结构

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_gallery_outer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
    <EditText
        android:id="@+id/tv_account"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_gravity="center"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="16dp"
        android:gravity="center"
        android:hint="输入用户名" />
    <EditText
        android:id="@+id/tv_pwd"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_gravity="center"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:gravity="center"
        android:hint="输入密码" />
    <Button
        android:id="@+id/btn_login"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_gravity="center"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:gravity="center"
        android:text="登录" />
</LinearLayout>

Activity代码


public class MVCActivity extends AppCompatActivity {
    TextView tvResult;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
         tvResult = findViewById(R.id.tv_result);
        TextView tvAccount = findViewById(R.id.tv_account);
        TextView tvPwd = findViewById(R.id.tv_pwd);
        Button btnLogin = findViewById(R.id.btn_login);
        MVCController mvcController = new MVCController();
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mvcController.login(tvAccount.getText().toString(),tvPwd.getText().toString(), MVCActivity.this);
            }
        });
    }
    public void loginSuccess(){
        tvResult.setText("登录成果:登录成功");
    }
    public void loginFail(){
        tvResult.setText("登录成果:登录失败");
    }
}

Controller代码

public class MVCController {
    MVCModel mvcModel;
    public MVCController() {
        mvcModel = new MVCModel();
    }
    public void login(String account, String pwd, MVCActivity loginActivity) {
        mvcModel.login(account, pwd, loginActivity);
    }
}

Model代码

public class MVCModel {
    public void login(String account, String pwd, MVCActivity loginActivity){
        if (account == null || account.length()==0) {
            loginActivity.loginFail();
        }
        if (pwd == null || pwd.length()==0) {
            loginActivity.loginFail();
        }
        if ("user123".equals(account) && "pwd123".equals(pwd)){
            loginActivity.loginSuccess();
        }
    }
}

完成代码阐明

在Activity中监听登录按钮的事情,接收到事情之后,调用Controller的登录办法处理登录逻辑,在Controller的登录办法中调用Model恳求网络数据(这里是模仿)判别是否登录成功,Model拿到登录成果后,调用Activity的办法刷新页面数据,展示登录成果。

优缺陷

长处:通过划分模块的办法,将体系分红了3个模块,视图层,事务层和数据层。 代码开发完成不再是只在一个代码文件中,必定程度便于程序开发。

缺陷:可是三个模块之间还存在很强的耦合关系。 不利于事务需求的更变和代码维护工作。

MVP

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

MVP架构是基于MVC的改善,将MVC的中Controller独立出来作为Presenter。 xml和Activity还是作为视图层, 视图层接收到页面数据,调用Presenter进行事务逻辑处理,Presenter调用Model进行数据处理,Model回传数据给Presenter,Presenter回传数据给View。数据的回传通过接口回调的办法来完成。

代码结构

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

IModel接口代码

public interface IModel {
    public boolean login(String account, String pwd);
}

IView接口代码

public interface IView {
    public void loginSuccess();
    public void loginFail();
}

Activity代码

public class MVPActivity extends AppCompatActivity implements IView {
    TextView tvResult;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        tvResult = findViewById(R.id.tv_result);
        TextView tvAccount = findViewById(R.id.tv_account);
        TextView tvPwd = findViewById(R.id.tv_pwd);
        Button btnLogin = findViewById(R.id.btn_login);
        MVPPresenter presenter = new MVPPresenter();
        presenter.setiView(this);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.login(tvAccount.getText().toString(), tvPwd.getText().toString());
            }
        });
    }
    public void loginSuccess() {
        tvResult.setText("登录成果:登录成功");
    }
    public void loginFail() {
        tvResult.setText("登录成果:登录失败");
    }
}

Model代码

public class MVPModel implements IModel {
    public boolean login(String account, String pwd) {
        if (account == null || account.length() == 0) {
            return false;
        }
        if (pwd == null || pwd.length() == 0) {
            return false;
        }
        if ("user123".equals(account) && "pwd123".equals(pwd)) {
            return true;
        }
        return false;
    }
}

Presenter代码

public class MVPPresenter {
    MVPModel model;
    public MVPPresenter() {
        model = new MVPModel();
    }
    IView iView;
    public void setiView(IView iView) {
        this.iView = iView;
    }
    public void login(String account, String pwd) {
        boolean loginResult = model.login(account, pwd);
        if (loginResult){
            iView.loginSuccess();
        }else {
            iView.loginFail();
        }
    }
}

完成代码阐明

界说了两个接口,IView和IModel, Activity和Model别离完成了这两个接口。 在Presenter中持有这两个实例。Presenter调用Model处理数据后,通过Iview的接口办法回调给Activity刷新页面。

优缺陷

从上面的代码可以看到,三个模块之间的通讯是通过接口完成的,在实践开发,界说的接口和办法会非常多。 导致很简略的一个页面功用也需求完成多个接口和办法。

长处便是通过Presenter,把MVC中的Controller代码抽出来了,而且Presenter作为View和Model通讯的桥梁,完成了Model和View的解耦。

MVVM

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

MVVM在MVP的基础上加入了双向绑定,使View可以感知ViewModel中的数据改动,ViewModel可以感知View数据的改动。

代码结构

一篇文章讲清楚Android中的MVC、MVP、MVVM架构 (附实现代码)

xml代码

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.domain.android.study.notes.architecture.mvvm.MVVMViewModel" />
    </data>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ll_gallery_outer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.result}"
            android:layout_gravity="center" />
        <EditText
            android:id="@+id/tv_account"
            android:layout_width="match_parent"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_height="40dp"
            android:hint="输入用户名"
            android:gravity="center"
            android:text="@={viewModel.account}"
            android:layout_gravity="center"
            android:layout_marginTop="20dp" />
        <EditText
            android:id="@+id/tv_pwd"
            android:layout_width="match_parent"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_height="40dp"
            android:hint="输入密码"
            android:text="@={viewModel.pwd}"
            android:gravity="center"
            android:layout_gravity="center" />
        <Button
            android:id="@+id/btn_login"
            android:layout_width="match_parent"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_height="40dp"
            android:text="登录"
            android:gravity="center"
            android:layout_gravity="center" />
    </LinearLayout>
</layout>

Activity代码

public class MVVMActivity extends AppCompatActivity {
    MVVMViewModel viewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityLoginBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_login);
        viewModel = ViewModelProviders.of(this).get(MVVMViewModel.class);
        binding.setVariable(BR.viewModel, viewModel);
        binding.setLifecycleOwner(this);
        binding.btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewModel.login();
            }
        });
    }
}

ViewModel代码

public class MVVMViewModel  extends ViewModel {
    public ObservableField<String> account = new ObservableField<>("");
    public ObservableField<String> pwd = new ObservableField<>("");
    public ObservableField<String> result = new ObservableField<>("");
    MVVMModel mvvmModel;
    public MVVMViewModel() {
         mvvmModel = new MVVMModel();
    }
    public void login(){
        boolean loginResult = mvvmModel.login(account.get(), pwd.get());
        result.set(loginResult ? "登录成果:成功" :"登录成果:失败");
    }
}

Model代码

public class MVVMModel {
    public boolean login(String account, String pwd) {
        if (account == null || account.length() == 0) {
            return false;
        }
        if (pwd == null || pwd.length() == 0) {
            return false;
        }
        if ("user123".equals(account) && "pwd123".equals(pwd)) {
            return true;
        }
        return false;
    }
}

留意

在本例MVVM架构完成中,用到了Android供给的data binding这个数据双向绑定结构。需求在APP模块的gralde文件中添加以下装备敞开:

 android {
 ...
 dataBinding {
        enabled true
    }
  ...
    }

完成代码阐明

通过Android供给的数据双向绑定库data binding 将Acitvity/xml视图层与ViewModel绑定。在xml布局文件中,通过@{}来表明单向绑定或者@={}来表明双向绑定。Activity接受到视图层的登录点击事情后,调用ViewModel处理登录事务逻辑,ViewModel通过双向数据绑定拿到到视图层输入的账号密码数据,调用Model处理数据,Model处理数据后,回传给ViewModel, ViewModel的数据改动,View感知后刷新页面。

留意

data binding通过观察者形式完成。 内部详细完成也是通过调用notify通知数据改动给观察者,notify调用了观察者完成的接口办法。

优缺陷

长处:通过数据双向绑定之后,咱们不在需求想MVP中写那么多接口回调办法区完成视图层和事务层的交互。事务层也不再持有视图层的引用。

缺陷:通过这种办法进行数据双向绑定后,xml中会多出一些标签、表达式、乃至和事务有点的简略核算代码。这不利于事务的逻辑的检查。而且由于双向绑定是data binding完成的。在这个过程中, 如果呈现bug导致数据没有被感知改动,不方便排错,由于xml不能debug调试。

03 总结

MVC、MVP、MVVM大体上都是把体系划分红3个模块:视图层、事务层、数据层。 可是他们的通讯办法、数据活动方向不一致,形成了不同的架构。 其后面发生的架构都是为了更好的解耦,解决已有架构的不足。每个架构都有自己的优缺陷,没有最好的架构,只要最合适的架构。