持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
1. 架构设计的目的
-
通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合
-
这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率,并且更容易进行后续的测试以及定位问题
-
但设计不能违背目的,对于不同量级的工程,具体架构的实现方式必然是不同的,切忌犯为了设计而设计,为了架构而架构的毛病
一个Android App如果只有3个Java文件,那只需要做点模块和层次的划分就可以,引入框架或者架构反而提高了工作量,降低了生产力; 如果当前开发的App最终代码量在10W行以上,本地需要进行复杂操作,同时也需要考虑到与其余的Android开发者以及后台开发人员之间的同步配合,那就需要在架构上进行一些思考!
2. MVC 设计架构
MVC 全名是 Model View Controller, 是 模型(model)-视图(view)-控制器(controller) 的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑
其中M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层
Android 中的 MVC
Android 中界面部分也采用了当前比较流行的 MVC 框架,在 Android 中:
-
视图层(View) 一般采用 XML 文件进行界面的描述,这些 XML 可以理解为 AndroidApp 的 View; 使用的时候可以非常方便的引入。同时便于后期界面的修改。逻辑中与界面对应的 id 不变化则代码不用修改,大大增强了代码的可维护性
-
控制层(Controller) Android 的控制层的重任通常落在了众多的 Activity 的肩上; 这句话也就暗含了不要在 Activity 中写代码,要通过 Activity 交割 Model 业务逻辑层处理,这样做的另外一个原因是 Android 中的 Actiivity 的响应时间是 5s,如果耗时的操作放在这里,程序就很容易被回收掉
-
模型层(Model) 我们针对业务模型,建立的数据结构和相关的类; 就可以理解为 AndroidApp 的 Model,Model 是与 View 无关,而与业务相关的(;对数据库的操作、对网络等的操作都应该在 Model 里面处理,当然对业务计算等操作也是必须放在的该层的;就是应用程序中二进制的数据 Controller 控制器 &View
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); }}
从上面代码可以看到,Activity 持有了 WeatherModel 模型的对象; 当用户有点击 Button 交互的时候,Activity 作为 Controller 控制层读取 View 视图层 EditTextView 的数据,然后向 Model 模型发起数据请求,也就是调用 WeatherModel 对象的方法 getWeather()方法
当Model模型处理数据结束后; 通过接口 OnWeatherListener 通知 View 视图层数据处理完毕,View 视图层该更新界面 UI 了;然后 View 视图层调用 displayResult()方法更新 UI。至此,整个 MVC 框架流程就在 Activity 中体现出来了。
Model 模型
来看看 WeatherModelImpl 代码实现
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 类中的方法发起网络请求,然后通过实现 OnWeatherListene r接口来获得网络请求的结果通知 View 视图层更新 UI;至此,Activity 就将 View 视图显示和 Model 模型数据处理隔离开了 activity 担当 contronller 完成了 model 和 view 之间的协调作用
至于这里为什么不直接设计成类里面的一个 getWeather()方法直接请求网络数据?
你考虑下这种情况: 现在代码中的网络请求是使用Volley框架来实现的,如果哪天老板非要你使用Afinal框架实现网络请求,你怎么解决问题?
难道是修改 getWeather()方法的实现? no no no,这样修改不仅破坏了以前的代码,而且还不利于维护, 考虑到以后代码的扩展和维护性,我们选择设计接口的方式来解决着一个问题,我们实现另外一个 WeatherModelWithAfinalImpl 类,继承自 WeatherModel,重写里面的方法,这样不仅保留了以前的 WeatherModelImpl 类请求网络方式,还增加了 WeatherModelWithAfinalImpl 类的请求方式;Activity 调用代码无需要任何修改
3.MVP 设计架构
在 App 开发过程中,经常出现的问题就是某一部分的代码量过大,虽然做了模块划分和接口隔离,但也很难完全避免; 从实践中看到,这更多的出现在 UI 部分,也就是 Activity 里
想象一下,一个 2000+ 行以上基本不带注释的 Activity; 我的第一反应就是想吐;Activity 内容过多的原因其实很好解释,因为 Activity 本身需要担负与用户之间的操作交互,界面的展示,不是单纯的 Controller 或 View。而且现在大部分的 Activity 还对整个 App 起到类似 IOS中的【ViewController】的作用,这又带入了大量的逻辑代码,造成 Activity 的臃肿。为了解决这个问题,让我们引入 MVP 框架
MVC 的缺点
在 Android 开发中,Activity 并不是一个标准的 MVC 模式中的 Controller,它的首要职责是加载应用的布局和初始化用户 界面,并接受并处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity 类的职责不断增加,以致变得庞大臃肿
什么是MVP?
MVP 从更早的 MVC 框架演变过来,与
.MVC 有一定的相似性:Controller/Presenter 负责逻辑的处理,Model提供数据,View 负责显示
MVP 框架由3部分组成:View 负责显示,Presenter 负责逻辑处理,Model 提供数据。在MVP 模式里通常包含3个要素(加上 View interface 是 4个):
-
View: 负责绘制 UI 元素、与用户进行交互(在Android中体现为Activity)
-
Model: 负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
-
Presenter: 作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
-
View interface: 需要View实现的接口,View 通过View interface与Presenter进行交互,降低耦合,方便进行单元测试
-
Tips: View interface 的必要性
回想一下你在开发 Android 应用时是如何对代码逻辑进行单元测试的?是否每次都要将应用部署到 Android 模拟器或真机上,然后通过模拟用 户操作进行测试?然而由于 Android 平台的特性,每次部署都耗费了大量的时间,这直接导致开发效率的降低。而在 MVP 模式中,处理复杂逻辑的 Presenter 是通过 interface与View (Activity) 进行交互的,这说明我们可以通过自定义类实现这个 interface 来模拟 Activity 的行为对 Presenter 进行单元测试,省去了大量的部署及测试的时间
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 的交互是通过接口来进行的,更有利于添加单元测试
因此我们可以发现MVP的优点如下:
1、模型与视图完全分离,我们可以修改视图而不影响模型;
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter 内部;
3、我们可以将一个 Presenter 用于多个视图,而不需要改变 Presenter 的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁;
4、如果我们把逻辑放在 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 层。
MVP 的变种:Passive View
MVP 的变种有很多,其中使用最广泛的是 Passive View 模式,即被动视图。在这种模式下, View 和 Model 之间不能直接交互,View 通过 Presenter 与 Model 打交道。Presenter 接受 View 的 UI 请求,完成简单的 UI 处理逻辑,并调用 Model 进行业务处理,并调用 View 将相应的结果反映出来;View 直接依赖 Presenter,但是 Presenter 间接依赖 View,它直接依赖的是 View 实现的接口
相对于 View 的被动,那 Presenter 就是主动的一方。对于 Presenter 的主动,有如下的理解:
Presenter 是整个 MVP 体系的控制中心,而不是单纯的处理 View 请求的人; View 仅仅是用户交互请求的汇报者,对于响应用户交互相关的逻辑和流程,View 不参与决策,真正的决策者是 Presenter
View 向 Presenter 发送用户交互请求应该采用这样的口吻: “我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”,不应该是这样:“我现在处理用户交互请求了,我知道该怎么办,但是我需要你的支持,因为实现业务逻辑的 Model 只信任你”
对于绑定到 View 上的数据,不应该是 View 从 Presenter 上“拉”回来的,应该是 Presenter 主动“推”给 View 的; View 尽可能不维护数据状态,因为其本身仅仅实现单纯的、独立的 UI 操作;Presenter 才是整个体系的协调者,它根据处理用于交互的逻辑给 View 和 Model 安排工作
MVP 架构存在的问题与解决办法
加入模板方法(Template Method) 转移逻辑操作之后可能部分较为复杂的 Activity 内代码量还是不少,于是需要在分层的基础上再加入模板方法(Template Method)
具体做法是在Activity内部分层; 其中最顶层为 BaseActivity,不做具体显示,而是提供一些基础样式,Dialog,ActionBar 在内的内容,展现给用户的 Activity 继承 BaseActivity,重写 BaseActivity 预留的方法。如有必要再进行二次继承,App 中 Activity 之间的继承次数最多不超过3次
模型层(Model)中的整体代码量是最大的; 一般由大量的 Package 组成,针对这部分需要做的就是在程序设计的过程中,做好模块的划分,进行接口隔离,在内部进行分层
强化Presenter的作用; 将所有逻辑操作都放在 Presenter 内也容易造成 Presenter 内的代码量过大,对于这点,有一个方法是在 UI 层和 Presenter 之间设置中介者 Mediator,将例如数据校验、组装在内的轻量级逻辑操作放在 Mediator 中;在 Presenter 和 Model 之间使用代理 Proxy;
上述两者分担一部分 Presenter 的逻辑操作,但整体框架的控制权还是在 Presenter 手中; Mediator 和 Proxy 不是必须的,只在 Presenter 负担过大时才建议使用
好了,以上就是今天要分享的内容,大家觉得有用的话,可以点赞分享一下;如果文章中有什么问题欢迎大家指正;欢迎在评论区或后台讨论哈~