Android 架构MVC MVP MVVM+实例

举报
帅次 发表于 2021/12/22 23:20:41 2021/12/22
【摘要】 前言         MVC、MVP和MVVM是软件比较常用的三种软件架构,这三种架构的目的都是分离,避免将过多的逻辑全部堆积在一个类中。         在Android中...

前言

        MVC、MVP和MVVM是软件比较常用的三种软件架构,这三种架构的目的都是分离,避免将过多的逻辑全部堆积在一个类中。

        在Android中,Activity中既有UI的相关处理逻辑,又有数据获取逻辑,从而导致Activity逻辑复杂不单一难以维护。

        为了一个应用可以更好的维护和扩展,我们需要很好的区分相关层级,要不然以后将数据获取方式从数据库变为网络获取时,我们需要去修改整个Activity。架构使得View和数据相互独立,我们把应用分成三个不同层级,这样我们就能够单独测试相关层级,使用架构能够把大多数逻辑从Activity中移除,方便进行单元测试。

MVC是什么?

        MVC是模型(Model)-视图(View)-控制器(Controller)的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码。其实Android Studio创建一个项目的模式就是一个简化的mvc模式。

Android中的MVC含义

  • Model:实体类(数据的获取、存储、数据状态变化)。

  • View:布局文件

  • Controller:Activity(处理数据、业务和UI)。

工作原理

  • 1.View接受用户的交互请求。

  • 2.View将请求转交给Controller。

  • 3.Controller操作Model进行数据更新。

  • 4.数据更新之后,Model通知View数据变化。

  • 5.View显示更新之后的数据。

MVC的缺点

        随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。

        为了解决MVC的缺点,MVP 框架被提出来。

MVP是什么

        MVP是MVC架构的一个演化版,全称是Model-View-Presenter。将MVC中的V和C结合生成MVP中的V,引入新的伙伴Presenter。

Android中的MVP含义

  • Model:实体类(数据的获取、存储、数据状态变化)。

  • View:布局文件+Activity。

  • Presenter:中介,负责完成View与Model间的交互和业务逻辑。

工作原理

  • 1.View 接收用户交互请求

  • 2.View 将请求转交给 Presenter(V调用P接口)

  • 3.Presenter 操作Model进行数据更新(P调用M接口)

  • 4.Model 通知Presenter数据发生变化(M调用P接口)

  • 5.Presenter 更新View数据(P执行接口,V相应回调)

MVP的优点

  • 1.复杂的逻辑处理放在Presenter进行处理,减少了Activity的臃肿。

  • 2.解耦。Model层与View层完全分离,修改V层不会影响M层,降低了耦合性。

  • 3.可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。

  • 4.Presenter层与View层的交互是通过接口来进行的,便于单元测试。

MVP的缺点

        维护困难。Presenter中除了业务逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,造成Presenter比较笨重,维护起来会比较困难。

MVVM是什么

        是 Model-View-ViewModel 的简写。MVVM与MVP的结构还是很相似的,就是将Presenter升级为ViewModel。在MVVM中,View层和Model层进行了双向绑定(即Data Binding),所以Model数据的更改会表现在View上,反之亦然。ViewModel就是用来根据具体情况处理View或Model的变化。

Android中的MVVM含义

  • Model:实体类(数据的获取、存储、数据状态变化)。

  • View:布局文件+Activity。

  • ViewModel: 关联层,将Model和View进行绑定,Model或View更改时,实时刷新对方。

工作原理

  • 1.View 接收用户交互请求

  • 2.View 将请求转交给ViewModel

  • 3.ViewModel 操作Model数据更新

  • 4.Model 更新完数据,通知ViewModel数据发生变化

  • 5.ViewModel 更新View数据

View/Model的变动,只要改其中一方,另一方都能够及时更新到

MVVM的优点

  • 1.提高可维护性。Data Binding可以实现双向的交互,使得视图和控制层之间的耦合程度进一步降低,分离更为彻底,同时减轻了Activity的压力。

  • 2.简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。

  • 3.ViewModle易于单元测试。

MVVM的缺点

  • 1.对于简单的项目,使用MVVM有点大材小用。

  • 2.对于过大的项目,数据绑定会导致内存开销大,影响性能。

  • 3.ViewModel和View的绑定,使页面异常追踪变得不方便。有可能是View出错,也有可能是ViewModel的业务逻辑有问题,也有可能是Model的数据出错。

MVP和MVC的最大区别

        在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter内部,而在MVC中View直接从Model中读取数据而不是通过 Controller。

如何选取框架

        本来是要每个模式写一个适用场景,最后想想每个人都有自己的理解,别被他人束缚了。

        一句话:适合自己的才是最好的!

实例

         就这么一个界面咱通过MVC、MVP、MVVM分别搭建一下。

MVC实例

代码结构

 1.在layout创建一个布局文件


  
  1.     <!--缩减版-->
  2.     <LinearLayout
  3.         ...>
  4.         <EditText
  5.             android:id="@+id/et_account"
  6.             .../>
  7.     </LinearLayout>
  8.     <LinearLayout
  9.         ...>
  10.         <EditText
  11.             android:id="@+id/et_password"
  12.             .../>
  13.     </LinearLayout>
  14.     <Button
  15.         android:id="@+id/btn_login"
  16.         .../>
  17.     <Button
  18.         android:id="@+id/btn_back"
  19.         .../>

2.实体类(User)


  
  1. public class User {
  2.     private String name;
  3.     private String password;
  4.     public User() {}
  5.     //set or get ...
  6.     public User(String name, String password) {
  7.         this.name = name;
  8.         this.password = password;
  9.     }
  10. }

3.MVCLoginActivity


  
  1. //用户点击事件
  2. mvcBinding.mcvLogin.btnLogin.setOnClickListener(new View.OnClickListener() {
  3.             @Override
  4.             public void onClick(View v) {
  5.                 user.setName(mvcBinding.mcvLogin.etAccount.getText().toString());
  6.                 user.setPassword(mvcBinding.mcvLogin.etPassword.getText().toString());
  7.                 login(user);
  8.             }
  9. });
  10. //逻辑处理
  11. private void login(User user){
  12.         if(!user.getName().isEmpty()&&!user.getPassword().isEmpty()){
  13.             if(user.getName().equals("scc001")&&user.getPassword().equals("111111"))
  14.             {
  15.                 Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
  16.             }else{
  17.                 Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
  18.             }
  19.         }else {
  20.             Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
  21.         }
  22.     }

MVP实例

代码结构

1.Model层

实体类bean,同MVC中的User类,就不贴代码浪费大家时间了。

Model层所要执行的业务逻辑


  
  1. /**
  2.   * 功能:接口,表示Model层所要执行的业务逻辑
  3.   */
  4. public interface LoginModel {
  5.     //User实体类;OnLoginFinishedListener presenter业务逻辑的返回结果
  6.     void login(User user, OnLoginFinishedListener listener);
  7. }

实现类(实现LoginModel接口)


  
  1. /**
  2.   * 功能:实现Model层逻辑
  3.   */
  4. public class LoginModelImpl implements LoginModel {
  5.     //第4步:验证帐号密码
  6.     @Override
  7.     public void login(User user, OnLoginFinishedListener listener) {
  8.         if(user.getName().isEmpty()||!user.getName().equals("scc001")){
  9.             //第5步:Model层里面回调Presenter层listener
  10.             listener.onUserNameError();
  11.         }else if(user.getPassword().isEmpty()||!user.getPassword().equals("111111")){
  12.             //第5步:Model层里面回调Presenter层listener
  13.             listener.onPasswordError();
  14.         }else {
  15.             //第5步:Model层里面回调Presenter层listener
  16.             listener.onSuccess();
  17.         }
  18.     }
  19. }

2.Presenter层

当Model层得到请求的结果,回调Presenter层,让Presenter层调用View层的接口方法。


  
  1. /**
  2.   * 功能:当Model层得到请求的结果,回调Presenter层,让Presenter层调用View层的接口方法。
  3.   */
  4. public interface OnLoginFinishedListener {
  5.     void onUserNameError();
  6.     void onPasswordError();
  7.     void onSuccess();
  8. }

完成登录的验证,以及销毁当前View。


  
  1. /**
  2.   * 功能:登录的Presenter的接口,实现类为LoginPresenterImpl,
  3.   * 完成登录的验证,以及销毁当前View。
  4.   */
  5. public interface LoginPresenter {
  6.     //完成登录的验证
  7.     void verifyData(User user);
  8.     //销毁当前View
  9.     void onDestroy();
  10. }

Presenter实现类,引入 LoginModel(model)和LoginView(view)的引用


  
  1. /**
  2.   * 功能:实现类,引入 LoginModel(model)和LoginView(view)的引用
  3.   */
  4. public class LoginPresenterImpl implements OnLoginFinishedListener, LoginPresenter {
  5.     //View层接口
  6.     private LoginView loginView;
  7.     //Model层接口
  8.     private LoginModel loginModel;
  9.     public LoginPresenterImpl(LoginView loginView) {
  10.         this.loginView = loginView;
  11.         this.loginModel = new LoginModelImpl();
  12.     }
  13.     //第6步:通过OnLoginFinishedListener验证结果回传到Presenter层
  14.     @Override
  15.     public void onUserNameError() {
  16.         if (loginView != null) {
  17.             //第7步:通过loginView回传到View层
  18.             loginView.setUserNameError();
  19.             loginView.hideProgress();
  20.         }
  21.     }
  22.     //第6步:通过OnLoginFinishedListener验证结果回传到Presenter层
  23.     @Override
  24.     public void onPasswordError() {
  25.         if (loginView != null) {
  26.             //第7步:通过loginView回传到View层
  27.             loginView.setPasswordError();
  28.             loginView.hideProgress();
  29.         }
  30.     }
  31.     //第6步:通过OnLoginFinishedListener验证结果回传到Presenter层
  32.     @Override
  33.     public void onSuccess() {
  34.         if (loginView != null) {
  35.             //第7步:通过loginView回传到View层
  36.             loginView.success();
  37.             loginView.hideProgress();
  38.         }
  39.     }
  40.     @Override
  41.     public void verifyData(User user) {
  42.         if (loginView != null) {
  43.             loginView.showProgress();
  44.         }
  45.         //第3步:调用model层LoginModel接口的login()方法
  46.         loginModel.login(user,this);
  47.     }
  48.     @Override
  49.     public void onDestroy() {
  50.         loginView = null;
  51.     }
  52. }

3.View层

布局文件同MVC中的View层,就不贴代码浪费大家时间了。

Presenter与View交互是通过接口。


  
  1. /**
  2.   * 功能:Presenter与View交互是通过接口。
  3.   * 接口中方法的定义是根据Activity用户交互需要展示的控件确定的。
  4.   */
  5. public interface LoginView {
  6.     //login是个耗时操作,加载中(一般用ProgressBar)
  7.     void showProgress();
  8.     //加载完成
  9.     void hideProgress();
  10.     //login账号失败给出提示
  11.     void setUserNameError();
  12.     //login密码失败给出提示
  13.     void setPasswordError();
  14.     //login成功
  15.     void success();
  16. }

MVPLoginActivity


  
  1. /**
  2.   * 功能:需要实现LoginView接口。
  3.   */
  4. public class MVPLoginActivity extends AppCompatActivity implements LoginView {
  5.     LoginPresenterImpl loginPresenterImpl;
  6.     @Override
  7.     protected void onCreate(@Nullable Bundle savedInstanceState) {
  8.         ...
  9.         //创建一个Presenter对象
  10.         loginPresenterImpl = new LoginPresenterImpl(MVPLoginActivity.this);
  11.         //第1步:用户点击登录
  12.         mvpBinding.mvpLogin.btnLogin.setOnClickListener(new View.OnClickListener() {
  13.             @Override
  14.             public void onClick(View v) {
  15.                 User user = new User();
  16.                 user.setName(mvpBinding.mvpLogin.etAccount.getText().toString());
  17.                 user.setPassword(mvpBinding.mvpLogin.etPassword.getText().toString());
  18.                 //第2步:调用Presenter接口中的验证方法
  19.                 loginPresenterImpl.verifyData(user);
  20.             }
  21.         });
  22.     }
  23.     @Override
  24.     public void showProgress() {
  25.         //加载中
  26.     }
  27.     @Override
  28.     public void hideProgress() {
  29.         //加载完成
  30.     }
  31.     @Override
  32.     public void setUserNameError() {
  33.         //第7步:通过loginView回传到View层
  34.         //账号错误
  35.         Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
  36.     }
  37.     @Override
  38.     public void setPasswordError() {
  39.         //第7步:通过loginView回传到View层
  40.         //密码错误
  41.         Toast.makeText(this,"登录失败",Toast.LENGTH_SHORT).show();
  42.     }
  43.     @Override
  44.     public void success() {
  45.         //第7步:通过loginView回传到View层
  46.         Toast.makeText(this,"登录成功",Toast.LENGTH_SHORT).show();
  47.         //登录成功
  48.     }
  49.     @Override
  50.     protected void onDestroy() {
  51.         super.onDestroy();
  52.         loginPresenterImpl.onDestroy();
  53.     }
  54. }

MVVM实例

1.Model层

实体类bean,继承BaseObservable


  
  1. public class User extends BaseObservable{
  2.     private String name;
  3.     private String password;
  4.     public User() {
  5.     }
  6.     //BR 的域则是通过在 get 方法上加 @Bindable 生成的
  7.     @Bindable
  8.     public String getName() {
  9.         return name;
  10.     }
  11.     public void setName(String name) {
  12.         this.name = name;
  13.         //刷新UI
  14.         //BR 的域则是通过在 get 方法上加 @Bindable 生成的
  15.         notifyPropertyChanged(BR.name);
  16.     }
  17.     @Bindable
  18.     public String getPassword() {
  19.         return password;
  20.     }
  21.     public void setPassword(String password) {
  22.         this.password = password;
  23.         //刷新UI
  24.         notifyPropertyChanged(BR.password);
  25.     }
  26.     public User(String name, String password) {
  27.         this.name = name;
  28.         this.password = password;
  29.     }
  30. }

2.ViewModel层

ViewModel类,继承自ViewModel


  
  1. public class LoginViewModel extends ViewModel {
  2.     public User user;
  3.     public User getUser() {
  4.         return user;
  5.     }
  6.     public void setUser(User user) {
  7.         this.user = user;
  8.     }
  9.     public void loginResult() {
  10.         if(user.getName().isEmpty()||!user.getName().equals("scc001")){
  11.             user.setName("scc005");
  12.         }else if(user.getPassword().isEmpty()||!user.getPassword().equals("111111")){
  13.             user.setName("scc004");
  14.         }else {
  15.             user.setName("scc003");
  16.         }
  17.         user.setPassword("111111");
  18.         Log.e("--SCC--","LoginViewModel:"+user.getName()+":"+user.getPassword());
  19.     }
  20. }

3.View层

先看布局文件,布局文件使用了DataBinding。


  
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  3.     <data>
  4.         <!--为引入的类从新起一个变量名,方便下面使用-->
  5.         <variable
  6.             name="loginViewModel"
  7.             type="com.scc.architecture.mvvm.viewmodel.LoginViewModel" />
  8.     </data>
  9.     <LinearLayout
  10.         ...>
  11.         <LinearLayout
  12.             ...>
  13.             <EditText
  14.                 android:id="@+id/et_account"
  15.                 ...
  16.                 android:text="@={loginViewModel.user.name}" />
  17.         </LinearLayout>
  18.         <LinearLayout
  19.             ...>
  20.             <EditText
  21.                 android:id="@+id/et_password"
  22.                 ...
  23.                 android:text="@={loginViewModel.user.password}" />
  24.         </LinearLayout>
  25.         <Button
  26.             android:id="@+id/btn_login"
  27.             ...
  28.             android:onClick="login"
  29.             android:text="@string/str_login" />
  30.         <Button
  31.             android:id="@+id/btn_back"
  32.             ...
  33.             android:onClick="back"
  34.             android:text="@string/str_back" />
  35.     </LinearLayout>
  36. </layout>

MVVMLoginActivity


  
  1. public class MVVMLoginActivity extends AppCompatActivity {
  2.     private LoginViewModel loginVM;
  3.     ActivityMvvmBinding mvvmBinding;
  4.     @Override
  5.     protected void onCreate(@Nullable Bundle savedInstanceState) {
  6.         super.onCreate(savedInstanceState);
  7.         //返回activity_mvvm的实体对象
  8.         mvvmBinding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
  9.         mvvmBinding.setLifecycleOwner(this);
  10.         loginVM = new LoginViewModel();
  11.         //创建数据源
  12.         User user = new User"scc001""111111");
  13.         //将数据源交给DataBinding
  14.         loginVM.setUser(user);
  15.         //设置et_account:scc001|et_password:111111
  16.         mvvmBinding.setLoginViewModel(loginVM);
  17.     }
  18.     public void login(View view){
  19.         loginVM.loginResult();
  20.     }
  21.     public void back(View view){
  22.         finish();
  23.     }
  24. }

        写到这里MVC、MCP、MVVM和实例基本写完了,但是感觉自己理解的不是很好,有大佬能指点就更好了。最后,希望对你有借鉴意义。

实例传送门

文章来源: shuaici.blog.csdn.net,作者:帅次,版权归原作者所有,如需转载,请联系作者。

原文链接:shuaici.blog.csdn.net/article/details/119756280

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。