Android中MVC与MVP学习笔记
2018-01-23 16:33
267 查看
第1部分:Android中的MVC
1、View
一般采用XML文件进行界面描述,这些XML可以理解为App中的View。2、Controller
控制层的重任通过落在了Activity的户上。但是一般不要把所有的控制代码都放到Activity中,一方面会造成Activity极为臃肿,不方便维护;另一方面,如果Activity中的代码量过大,响应时间太长,会造成ANR。3、Model
对数据(数据库、网络数据、文件等)的操作都应该放到Model层。Model是与View无关,而与业务有关的。4、MVC示例
View层
![](http://assets.tianmaying.com/md-image/91bd52d637fd71834128144f058551cb.png)
Controller层
public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener { private WeatherModel weatherModel; private EditText cityNOInput; private TextView city; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); weatherModel = new WeatherModelImpl(); initView(); } //初始化View private void initView() { cityNOInput = findView(R.id.et_city_no); city = findView(R.id.tv_city); ... findView(R.id.btn_go).setOnClickListener(this); } //显示结果 public void displayResult(Weather weather) { WeatherInfo weatherInfo = weather.getWeatherinfo(); city.setText(weatherInfo.getCity()); ... } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_go: weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); break; } } @Override public void onSuccess(Weather weather) { displayResult(weather); } @Override public void onError() { Toast.makeText(this, 获取天气信息失败, Toast.LENGTH_SHORT).show(); } private T findView(int id) { return (T) findViewById(id); } }
Model层
public interface WeatherModel { void getWeather(String cityNumber, OnWeatherListener listener); } ................ public class WeatherModelImpl implements WeatherModel { /*这部分代码范例有问题,网络访问不应该在Model中,应该把网络访问换成从数据库读取*/ @Override public void getWeather(String cityNumber, final OnWeatherListener listener) { /*数据层操作*/ VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, Weather.class, new Response.Listener<weather>() { @Override public void onResponse(Weather weather) { if (weather != null) { listener.onSuccess(weather); } else { listener.onError(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { listener.onError(); } }); } }
说明
以上代码看出,这里设计了一个WeatherModel模型接口,然后实现了接口WeatherModelImpl类。controller控制器activity调用WeatherModelImpl类中的方法发起网络请求,然后通过实现OnWeatherListener接口来获得网络请求的结果通知View视图层更新UI 。至此,Activity就将View视图显示和Model模型数据处理隔离开了。activity担当contronller完成了model和view之间的协调作用。至于这里为什么不直接设计成类里面的一个getWeather()方法直接请求网络数据?你考虑下这种情况:现在代码中的网络请求是使用Volley框架来实现的,如果哪天老板非要你使用Afinal框架实现网络请求,你怎么解决问题?难道是修改 getWeather()方法的实现? no no no,这样修改不仅破坏了以前的代码,而且还不利于维护, 考虑到以后代码的扩展和维护性,我们选择设计接口的方式来解决着一个问题,我们实现另外一个WeatherModelWithAfinalImpl类,继承自WeatherModel,重写里面的方法,这样不仅保留了以前的WeatherModelImpl类请求网络方式,还增加了WeatherModelWithAfinalImpl类的请求方式。Activity调用代码无需要任何修改。
第2部分:MVP设计架构
在App开发过程中,经常出现的问题是某一部分代码量过大,虽然做了模块划分和接口隔离,但也很难完全避免。从实践中看到,这更多的出现在UI部分,也就是Activity里。想象一下,一个2000+行以上基本不带注释的Activity,我的第一反应就是想吐。Activity内容过多的原因其实很好解释,因为Activity本身需要担负与用户之间的操作交互,界面的展示,不是单纯的Controller或View。而且现在大部分的Activity还对整个App起到类似IOS中的【ViewController】的作用,这又带入了大量的逻辑代码,造成Activity的臃肿。为了解决这个问题,让我们引入MVP框架。MVC的缺点
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户 界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。什么是MVP
![](https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1057654571,833047008&fm=27&gp=0.jpg)
MVP框架由3部分组成:View负责显示,Presenter负责逻辑处理,Model提供数据。在MVP模式里通常包含3个要素(加上View interface是4个):
View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity)
Mo
f48b
del:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试
View interface的必要性
回想一下你在开发Android应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到Android模拟器或真机上,然后通过模拟用 户操作进行测试?然而由于Android平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在MVP模式中,处理复杂逻辑的Presenter是通过interface与View(Activity)进行交互的,这说明我们可以通过自定义类实现这个interface来模拟Activity的行为对Presenter进行单元测试,省去了大量的部署及测试的时间。第3部分:MVC → MVP
当我们将Activity复杂的逻辑处理移至另外的一个类(Presenter)中时,Activity其实就是MVP模式中的View,它负责UI元素的初始化,建立UI元素与Presenter的关联(Listener之类),同时自己也会处理一些简单的逻辑(复杂的逻辑交由 Presenter处理)。MVP的Presenter是框架的控制者,承担了大量的逻辑操作,而MVC的Controller更多时候承担一种转发的作用。因此在App中引入MVP的原因,是为了将此前在Activty中包含的大量逻辑操作放到控制层中,避免Activity的臃肿。
两者的比较
(最主要区别)View与Model并不直接交互,而是通过与Presenter交互来与Model间接交互。而在MVC中View可以与Model直接交互通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。而Controller是基于行为的,并且可以被多个View共享,Controller可以负责决定显示哪个View
Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
![](http://pic001.cnblogs.com/images/2012/1/2012040113391482.jpg)
MVP的优点
模型与视图完全分离,我们可以修改视图而不影响模型;可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;
如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
总结:
具体到Android App中,一般可以将App根据程序的结构进行纵向划分,根据MVP可以将App分别为模型层(M),UI层(V)和逻辑层(P)。UI层一般包括Activity,Fragment,Adapter等直接和UI相关的类,UI层的Activity在启动之后实例化相应的Presenter,App的控制权后移,由UI转移到Presenter,两者之间的通信通过BroadCast、Handler或者接口完成,只传递事件和结果。
举个简单的例子,UI层通知逻辑层(Presenter)用户点击了一个Button,逻辑层(Presenter)自己决定应该用什么行为进行响应,该找哪个模型(Model)去做这件事,最后逻辑层(Presenter)将完成的结果更新到UI层。
第4部分:MVP的变种:Passive View
MVP的变种有很多,其中使用最广泛的是Passive View模式,即被动视图。在这种模式下,View和Model之间不能直接交互,View通过Presenter与Model打交道。Presenter接受View的UI请求,完成简单的UI处理逻辑,并调用Model进行业务处理,并调用View将相应的结果反映出来。View直接依赖Presenter,但是Presenter间接依赖View,它直接依赖的是View实现的接口。![](http://assets.tianmaying.com/md-image/f7002cd0e8951e46fd963bff0a0081d8.jpg)
相对于View的被动,那Presenter就是主动的一方。对于Presenter的主动,有如下的理解:
Presenter是整个MVP体系的控制中心,而不是单纯的处理View请求的人;
View仅仅是用户交互请求的汇报者,对于响应用户交互相关的逻辑和流程,View不参与决策,真正的决策者是Presenter;
View向Presenter发送用户交互请求应该采用这样的口吻:“我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”,不应该是这样:“我现在处理用户交互请求了,我知道该怎么办,但是我需要你的支持,因为实现业务逻辑的Model只信任你”;
对于绑定到View上的数据,不应该是View从Presenter上“拉”回来的,应该是Presenter主动“推”给View的;
View尽可能不维护数据状态,因为其本身仅仅实现单纯的、独立的UI操作;Presenter才是整个体系的协调者,它根据处理用于交互的逻辑给View和Model安排工作。
第5部分:MVP架构存在的问题与解决办法
问题1:转移逻辑操作之后可能部分较为复杂的Activity内代码量还是不少。解决思路:在分层的基础上再加入模板方法(Template Method)。
具体操作:
在Activity内部分层。
其中最顶层为BaseActivity,不做具体显示,而是提供一些基础样式,Dialog,ActionBar在内的内容。
展现给用户的Activity继承BaseActivity,重写BaseActivity预留的方法。
如有必要再进行二次继承,App中Activity之间的继承次数最多不超过3次。
问题2:模型层(Model)中的整体代码量是最大的。
解决思路:内容分层。
具体操作:做好模块的划分,进行接口隔离,在内部进行分层。
问题3:Presenter中的逻辑过重
解决思路:在UI层和Presenter之间设置中介者Mediator。
具体操作:
将例如数据校验、组装在内的轻量级逻辑操作放在Mediator中;
在Presenter和Model之间使用代理Proxy;
通过上述两者分担一部分Presenter的逻辑操作,但整体框架的控制权还是在Presenter手中。Mediator和Proxy不是必须的,只在Presenter负担过大时才建议使用。
最终架构图:
![](http://assets.tianmaying.com/md-image/ee7b3f65d6ff23453e78193b6a37d79b.jpg)
第6部分:MVP代码示例
包结构
![](http://assets.tianmaying.com/md-image/6dca6529ad6a21f5081838638a408d82.png)
代码示例
Bean
public class UserBean { private String mFirstName; private String mLastName; public UserBean(String firstName, String lastName) { this. mFirstName = firstName; this. mLastName = lastName; } public String getFirstName() { return mFirstName; } public String getLastName() { return mLastName; } }
Model
public interface IUserModel { void setID(int id); void setFirstName(String firstName); void setLastName(String lastName); int getID(); UserBean load(int id);// 通过id读取user信息,返回一个UserBean }
Presenter
public class UserPresenter { private IUserView mUserView; private IUserModel mUserModel; public UserPresenter(IUserView view) { mUserView = view; mUserModel = new UserModel(); } public void saveUser( int id, String firstName, String lastName) { mUserModel.setID(id); mUserModel.setFirstName(firstName); mUserModel.setLastName(lastName); } public void loadUser( int id) { UserBean user = mUserModel.load(id); mUserView.setFirstName(user.getFirstName()); // 通过调用IUserView的方法来更新显示 mUserView.setLastName(user.getLastName()); } }
View
public interface IUserView { int getID(); String getFristName(); String getLastName(); void setFirstName(String firstName); void setLastName(String lastName); }
public class MainActivity extends Activity implements OnClickListener,IUserView { UserPresenter presenter; EditText id,first,last; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. activity_main); findViewById(R.id. save).setOnClickListener( this); findViewById(R.id. load).setOnClickListener( this); id = (EditText) findViewById(R.id. id); first = (EditText) findViewById(R.id. first); last = (EditText) findViewById(R.id. last); presenter = new UserPresenter( this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id. save: presenter.saveUser(getID(), getFristName(), getLastName()); break; case R.id. load: presenter.loadUser(getID()); break; default: break; } } @Override public int getID() { return new Integer( id.getText().toString()); } @Override public String getFristName() { return first.getText().toString(); } @Override public String getLastName() { return last.getText().toString(); } @Override public void setFirstName(String firstName) { first.setText(firstName); } @Override public void setLastName(String lastName) { last.setText(lastName); } }
第7部分:总结
Activity及从MVC中的Controller中解放出来了,这会Activity主要做显示View的作用和用户交互。每个Activity可以根据自己显示View的不同实现View视图接口IUserView。通过对比同一实例的MVC与MVP的代码,可以证实MVP模式的一些优点:
在MVP中,Activity的代码不臃肿;
在MVP中,Model(IUserModel的实现类)的改动不会影响Activity(View),两者也互不干涉,而在MVC中会;
在MVP中,IUserView这个接口可以实现方便地对Presenter的测试;
在MVP中,UserPresenter可以用于多个视图,但是在MVC中的Activity就不行。
相关文章推荐
- Android 官方示例:android-architecture 学习笔记(六)之todo-mvp-contentproviders
- Android 学习笔记 —— Android 四大组件 与 MVC 架构模式
- Android 官方示例:android-architecture 学习笔记(三)之todo-mvp-loaders
- Android笔记:Android App的设计架构:MVC,MVP,MVVM与架构经验谈
- Android MVP学习笔记
- android mvp 学习笔记(一)
- Android 官方示例:android-architecture 学习笔记(五)之todo-mvp-clean
- 学习笔记:Android里面的MVC模式与MVP模式
- Android学习(十一) 关于MVP与MVC
- Android进阶笔记07:用MVP架构开发Android应用(MVC 和 MVP)
- Android-缓存网络图片(MVP模式)学习笔记
- Android 框架 MVC、MVP、MVVM学习笔记
- android中mvc,mvp,mvvm学习
- android 学习mvc 和 mvp 和 mvvm参考项目
- Android 官方示例:android-architecture 学习笔记(七)之todo-mvp-dagger
- Android 官方示例:android-architecture 学习笔记(二)之todo-mvp
- Android IMF 学习笔记三
- Android(OPhone) 学习笔记 - 代码基础
- ASP.NET MVC 1.0学习笔记
- MVC WebCast学习笔记