博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android MVC && MVP && MVVM分析和对照
阅读量:4553 次
发布时间:2019-06-08

本文共 15250 字,大约阅读时间需要 50 分钟。

  面试的时候被问到这个问题。用过,也了解过。可是还是不够深入,总结一下。

  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
(), 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); }}

  代码。这个样例逻辑非常easy,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的业务量会显的非常大,三层之间的交互关系为:

  1. View接受用户的交互请求
  2. View将请求转交给Presenter
  3. Presenter操作Model进行数据库更新
  4. 数据更新之后,Model通知Presenter数据发生变化
  5. 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层不须要做不论什么改动。

样例

  MVP 的写法就有非常多了,不同人对于 MVP 的写法各有不同,在遵循基础上都是能够的,这里我就以 google 大大的官方 demo 和一个外国大神的 MVP demo 为例来分析一下。用哪种形式不要纠结。最重要的是自己用起来顺手。

google 官方写法

  先贴出来源代码地址:。分析一下:

  这里写图片描写叙述
  在该 demo 中,有一个 BaseView 和 BasePresenter 。而且使用泛型来定义,作用是定义该模块不同 View 和 Presenter 的基础行为:
BaseView.class

public interface BaseView
{ void setPresenter(T presenter);}

BasePresenter.class

public interface BasePresenter {    void start();}

之后的每个页面的 View 和 Presenter 都要继承自 BaseView 和 BaseAdapter ,在 demo 中。View 和 Presenter 的接口类都定义在一个 TasksContract 类中:

TasksContract.class

public interface TasksContract {
interface View extends BaseView
{
void setLoadingIndicator(boolean active); void showTasks(List
tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter {
void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); }}

之后就是实现这两个基础接口了,demo 中使用的是 Fragment 作为 View 的角色。把逻辑从 Activity 中抽离出来,这个看自己的编程习惯吧,仅仅使用 Activity 还是使用 Fragment。先看看 Activity 的代码:

AppCompatActivity.class

public class TasksActivity extends AppCompatActivity {
private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY"; private DrawerLayout mDrawerLayout; private TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); // Set up the toolbar. Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar ab = getSupportActionBar(); ab.setHomeAsUpIndicator(R.drawable.ic_menu); ab.setDisplayHomeAsUpEnabled(true); // Set up the navigation drawer. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); if (navigationView != null) { setupDrawerContent(navigationView); } TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } // Create the presenter mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); // Load previously saved state, if available. if (savedInstanceState != null) { TasksFilterType currentFiltering = (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); mTasksPresenter.setFiltering(currentFiltering); } }...}

能够看到代码中,通过 Fragment 的静态方法获取到一个 Fragment 。而且加入到 activity 中,那么如今有一个疑问了。 Presenter 是怎样设置到 Fragment 中的呢?我们接下来看看 Presenter 类:

TasksPresenter.class

public class TasksPresenter implements TasksContract.Presenter {
... public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); } @Override public void start() { loadTasks(false); } ...}

能够看到是在 Presenter 的构造函数中 set 的。这也就没问题了,官方的 MVP 框架就介绍完了,大家去看一下源代码就非常清楚了。

其它可參考写法

  另外一个外国人写的样例。分析一下:

  这里写图片描写叙述
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这个框架,可是好像侵入性太强,普及程度不高,所以能够看看这篇博客。它使用了databinding依赖库进行处理。讲的非常好。
  也能够看看,外国人写的一个库,进行了非常多其它的封装。能够參考一下:
这里写图片描写叙述
里面的介绍也非常清楚。

MVC VS MVP VS 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
  1. Controller是基于行为。而且能够在view之间共享
  2. Controller负责接收用户交互等操作,而且决定须要显示的视图。
MVP
  1. View和Model更加的解耦了,Presenter负责绑定Model到View。
  2. 复杂的View能够相应多个Persenter。
  3. Presenter保留有View层的事件逻辑。全部的点击之类的事件都直接托付给Presenter。

  4. Presenter通过接口直接和View层解耦。所以更加方便的进行View的单元測试。
  5. Presenter和其它两层都是双向调用的。
  6. 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
  1. 用户直接交互的是View。

  2. View和ViewModel是多对一的关系。
  3. View有ViewModel的引用,可是ViewModel没有不论什么关于View的信息。

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

引用

转载于:https://www.cnblogs.com/jzdwajue/p/7128195.html

你可能感兴趣的文章
Shell 正则表达式
查看>>
Docker run命令参数整理
查看>>
qt-opencv配置mingw编译器
查看>>
CSS之Medial Queries的另一用法:实现IE hack的方法
查看>>
oo第三单元总结
查看>>
linux-CentOS6.4下安装oracle11g详解
查看>>
tomcat禁用webdav
查看>>
还是畅通工程
查看>>
sql字段组合唯一
查看>>
电脑软件故障排除2014年2月16日[修正版]
查看>>
YARN的笔记
查看>>
[机器学习]回归--(Simple LR and Multiple LR)
查看>>
javascript单线程,异步与执行机制
查看>>
腹部训练
查看>>
android ImageView scaleType属性
查看>>
day 4 继承
查看>>
14 模块
查看>>
4- 算法练习leetcode.com
查看>>
02-替换空格
查看>>
许式伟、张宴——系统架构运维思路对话
查看>>