下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

在android开发中学习MVC 、MVP和MVVM详细分析与对比

作者:课课家教育     来源: http://www.kokojia.com点击数:1088发布时间: 2019-02-28 14:49:04

标签: androidandroidappandroid开发

  这个移动互联网的时代,学习移动端开发是个这个市场非常热门的,移动端操作系统当下比较流行的Android、ios等等,但移动智能机Android操作系统在国内的市场是相当大,因此,想要往软件开发等职业方向发展的话,学习Android开发是一个不错的选择。学习Android开发中有三个软件开发比较常用的架构,分别是MVC,MVP和MVVM,那么本文就介绍一下MVC,MVP和MVVM的相关知识。

  MVC,MVP和MVVM是软件比较常用的三种软件架构,这三种架构的目的都是分离关注,避免将过多的逻辑全部堆积在一个类中,以android为例,在activity中既有UI的相关处理逻辑,又有数据获取逻辑,从而导致activity逻辑复杂不单一难以维护。为了一个应用可以更好的维护和扩展,我们需要很好的区分相关层级,要不然以后将数据获取方式从数据库变为网络获取时,我们需要去修改整个activity。架构使得view和数据相互独立,我们把应用分成三个不同层级,这样我们就能够单独测试相关层级,使用架构能够把大多数逻辑从activity中移除,方便进行单元测试。

  MVC

  Model View Controller模式,MVC将应用分成三个主要层级:Model,View和Controller,它强制将逻辑进行分离,数据结构和Controller逻辑与UI是解耦的,所以测试相关模块变的更简单。

  其实android app的界面开发部分已经是遵从MVC模式的,

  视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入,当然,也可以使用JavaScript+HTML等的方式作为View层,他的职责就是负责显示从Controller上获取到的数据(但是xml布局作为View来说功能很无力,所以通常Activity也会承担一部分View的工作)。

  控制层(Controller):Android的控制层的重任通常落在了众多的Activity的肩上,他们从模型层获取数据,将获取到的数据绑定到view上,并且还需要监听用户的输入等操作。

  模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算,变更等操作也是必须放在的该层的。

  MVC模式具体表现在android上的效果如下图所示:

  也可以看看 这个视频,介绍的不错:

  还有另外的,android 中的adapter也是使用的MVC模式,自定义的adapter相当于Controller。

  例子

  以一个获取天气的例子来说,xml布局可视为View层;Activity为Controller层,控制用户输入,将Model层获取到的数据展示到View层;Model层的实体类当然就是用来获取网络数据了。

  Model层

  WeatherModel.class接口

  public interface WeatherModel {

  void getWeather(OnLoadWeatherCallback callback);

  }

  WeatherModelImpl.class类

  public class WeatherModelImpl implements WeatherModel{

  private Context mContext;

  public WeatherModelImpl(Context context){

  mContext = context;

  }

  @Override

  public void getWeather(final OnLoadWeatherCallback callback) {

  NetApi.getInstance().jsonObjectRequest(mContext, "http://www.weather.com.cn/data/sk/101010100.html",

  new HashMap<string, string="">(), new BaseNetApi.OnNetCallback() {

  @Override

  public void onSuccess(JSONObject jsonObject) {

  try {

  jsonObject = new JSONObject(jsonObject.getString("weatherinfo"));

  WeatherInfo info = new WeatherInfo();

  info.city = jsonObject.getString("city");

  info.temp = Double.parseDouble(jsonObject.getString("temp"));

  info.WD = jsonObject.getString("WD");

  info.WS = jsonObject.getString("WS");

  info.time = jsonObject.getString("time");

  callback.onLoadSuccess(info);

  } catch (JSONException e) {

  L.e(e);

  }

  }

  @Override

  public void onFail(NetError netError) {

  callback.onError(netError);

  }

  });

  }

  }

  Controller层

  WeatherActivity.class类

  public class WeatherActivity extends BaseActivity implements OnLoadWeatherCallback{

  private TextView tv_name;

  private TextView tv_temperature;

  private TextView tv_wind_d;

  private TextView tv_wind_s;

  private TextView tv_time;

  private LoadingDialog ld;

  private WeatherModel weatherModel;

  @Override

  protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_weather);

  tv_name = (TextView) findViewById(R.id.tv_name);

  tv_temperature = (TextView) findViewById(R.id.tv_temperature);

  tv_wind_d = (TextView) findViewById(R.id.tv_wind_d);

  tv_wind_s = (TextView) findViewById(R.id.tv_wind_s);

  tv_time = (TextView) findViewById(R.id.tv_time);

  weatherModel = new WeatherModelImpl(this);

  ld = new LoadingDialog(this);

  ld.setLoadingText("正在获取天气...");

  ld.show();

  weatherModel.getWeather(this);

  }

  private void onShowWeather(WeatherInfo weatherInfo){

  tv_name.setText(weatherInfo.city);

  tv_temperature.setText(weatherInfo.temp+"");

  tv_wind_d.setText(weatherInfo.WD);

  tv_wind_s.setText(weatherInfo.WS);

  tv_time.setText(weatherInfo.time);

  }

  @Override

  public void onLoadSuccess(WeatherInfo info) {

  ld.dismiss();

  onShowWeather(info);

  }

  @Override

  public void onError(NetError error) {

  ld.dismiss();

  T.getInstance().showShort(error.errorCode +" "+ error.errorMessage);

  }

  }

   这个例子逻辑很简单,Controller层的Activity持有Model层WeatherModel的对象,然后通过该对象去获取数据,获取到数据之后,通过View层去显示。但是上面有提到过xml布局文件作为View层,其实能做的事情特别少,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,造成了Activity既像View又像Controller(在逻辑简单的情况下,视图层和控制层写在一起貌似可以减少几个类= =),所以有这么一句话

  Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.

  所以这时候可以继续把Activity拆分,Activity只控制view和接受用户的输入,另外新建一个Controller类,这个类不能继承任何Android自带类,用来将逻辑拆分出来,避免Activity的难以维护,具体可以看看这个例子

  MVP

  Model, View and Presenter模式,MVP模式和MVC模式类似,是由MVC演变而来,MVP将Controller变成Presenter,并且改变了通信方向,这个模式将应用分为三个主要层级:Model, View and Presenter。

  可以看到Presenter与Model,Presenter与View的通信都是双向的,View不与Model发生关系,都是通过Presenter来传递,所以Presenter的业务量会显的非常大,三层之间的交互关系为:

  View接受用户的交互请求

  View将请求转交给Presenter

  Presenter操作Model进行数据库更新

  数据更新之后,Model通知Presenter数据发生变化

  Presenter更新View层的显示

  Model和View层之间是没有交互的,这是和MVC不同的一点:

  Model层

  该层通常是用来处理业务逻辑和实体模型。

  View层

  通常是一个Activity或者Fragment或者View,这取决于应用的结构,它会持有一个Presenter层的引用,所以View唯一做的事情就是在有用户交互等操作时调用Presenter层的方法。

  Presenter层

  该层用来作为一个中间层的角色,它接受Model层的数据,并且处理之后传递给View层,还需要处理View层的用户交互等操作。 View和Presenter的一对一关系意味着一个View只能映射到一个Presenter上,并且View只有Presenter的引用,没有Model的引用,所以Presenter和View是一个双向的交互。Presenter不管View层的UI布局,View的UI布局变更,Presenter层不需要做任何修改。

  例子

  这个一个外国人写的例子,分析一下:

  LoginActivity继承自LoginView;LoginPresenterImpl继承自LoginPresenter;LoginInteractorImpl继承自LoginInteractor,所以MVP模式三个层次之间是通过接口来进行交互的,看看源码:

  LoginInteractorImpl.class类

  public class LoginInteractorImpl implements LoginInteractor {

  @Override

  public void login(final String username, final String password, final OnLoginFinishedListener listener) {

  // Mock login. I'm creating a handler to delay the answer a couple of seconds

  new Handler().postDelayed(new Runnable() {

  @Override public void run() {

  boolean error = false;

  if (TextUtils.isEmpty(username)){

  listener.onUsernameError();

  error = true;

  }

  if (TextUtils.isEmpty(password)){

  listener.onPasswordError();

  error = true;

  }

  if (!error){

  listener.onSuccess();

  }

  }

  }, 2000);

  }

  }

  LoginActivity.class类

  public class LoginActivity extends Activity implements LoginView, View.OnClickListener {

  private ProgressBar progressBar;

  private EditText username;

  private EditText password;

  private LoginPresenter presenter;

  @Override

  protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_login);

  progressBar = (ProgressBar) findViewById(R.id.progress);

  username = (EditText) findViewById(R.id.username);

  password = (EditText) findViewById(R.id.password);

  findViewById(R.id.button).setOnClickListener(this);

  presenter = new LoginPresenterImpl(this);

  }

  @Override protected void onDestroy() {

  presenter.onDestroy();

  super.onDestroy();

  }

  @Override public void showProgress() {

  progressBar.setVisibility(View.VISIBLE);

  }

  @Override public void hideProgress() {

  progressBar.setVisibility(View.GONE);

  }

  @Override public void setUsernameError() {

  username.setError(getString(R.string.username_error));

  }

  @Override public void setPasswordError() {

  password.setError(getString(R.string.password_error));

  }

  @Override public void navigateToHome() {

  startActivity(new Intent(this, MainActivity.class));

  finish();

  }

  @Override public void onClick(View v) {

  presenter.validateCredentials(username.getText().toString(), password.getText().toString());

  }

  }

  LoginPresenterImpl.class类

  public class LoginPresenterImpl implements LoginPresenter, OnLoginFinishedListener {

  private LoginView loginView;

  private LoginInteractor loginInteractor;

  public LoginPresenterImpl(LoginView loginView) {

  this.loginView = loginView;

  this.loginInteractor = new LoginInteractorImpl();

  }

  @Override public void validateCredentials(String username, String password) {

  if (loginView != null) {

  loginView.showProgress();

  }

  loginInteractor.login(username, password, this);

  }

  @Override public void onDestroy() {

  loginView = null;

  }

  @Override public void onUsernameError() {

  if (loginView != null) {

  loginView.setUsernameError();

  loginView.hideProgress();

  }

  }

  @Override public void onPasswordError() {

  if (loginView != null) {

  loginView.setPasswordError();

  loginView.hideProgress();

  }

  }

  @Override public void onSuccess() {

  if (loginView != null) {

  loginView.navigateToHome();

  }

  }

  }

  代码层次很清楚,View层接受用户的点击操作,回调Presenter层的相关接口,Presenter层再调用到Model层去执行登录操作,同时修改View层的Progress显示情况,Model层执行完登录操作之后,回调到Presenter层的对应接口,Presenter再去对View层的布局进行相应的修改。

  MVVM

  Model,View and ViewModel模式,MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致,ViewModel可以理解成是View的数据模型和Presenter的合体,MVVM采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然,这种模式实际上是框架替应用开发者做了一些工作,开发者只需要较少的代码就能实现比较复杂的交互。

  Model

  类似MVP

  View

  类似MVP

  ViewModel

  注意这里的“Model”指的是View的Model,跟上面那个Model不是一回事。所谓View的Model就是包含View的一些数据属性和操作的东西。

  例子

  这种模式的关键技术就是数据绑定(data binding)。

  在android中已经有了相应的插件框架,比如 RoboBinding这个框架,但是好像侵入性太强,普及程度不高,所以可以看看全面介绍Android的MVVM框架 - 数据绑定这篇博客,它使用了databinding依赖库进行处理,讲的很好。

  也可以看看,外国人写的一个库,进行了很多其他的封装,可以参考一下:

  里面的介绍也非常清楚。

  MVC、 MVP、和MVVM

  MVP模式是从MVC模式演变来的,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示,所以他们之间并没有特别大的不同,都是用来将View和Model之间松耦合。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中是允许Model和View进行交互的。还有重要的一点就是Presenter与View之间的交互是通过接口的。MVVM是通过MVP演变而来的,主要用了data binding来实现双向交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。

  关键点总结:

  MVC

  Controller是基于行为,并且能够在view之间共享

  Controller负责接收用户交互等操作,并且决定需要显示的视图。

  MVP

  View和Model更加的解耦了,Presenter负责绑定Model到View。

  复杂的View可以对应多个Persenter。

  Presenter保留有View层的事件逻辑,所有的点击之类的事件都直接委托给Presenter。

  Presenter通过接口直接和View层解耦,所以更加方便的进行View的单元测试。

  Presenter和其他两层都是双向调用的。

  MVP有两种实现方式:”Passive View”,View基本包含0逻辑, Presenter作为View和Model的中间人,View和Model相互隔离,View和Model没有直接的数据绑定,取而代之的是View提供相关的setter方法供Persenter去调用,这么做的好处是View和Model干净的分离开了,所以更好的进行相关测试,缺点是需要提供很多的setter方法;”Supervising Controller”,Persenter处理用户交互等的操作,View和Model直接通过数据绑定连接,这种模式下,Persenter的任务就是将实体直接通过Model层传递给View层,这种方法的好处就是代码量少了,但是缺点就是测试难度增大,并且View的封装性变低。

  MVVM

  用户直接交互的是View。

  View和ViewModel是多对一的关系。

  View有ViewModel的引用,但是ViewModel没有任何关于View的信息。

  支持View和ViewModel的双向数据绑定。

  引用

     上文通过详细的文字和代码的形式来介绍MVC,MVP和MVVM的相关知识,还分别举例说分析以及对比MVC,MVP和MVVM的相关内容。想要再深入学习MVC,MVP和MVVM的相关知识,可以在网上搜索相关资料,做深入研究。

赞(0)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程