前言
上一篇文章
【Android 进阶】ButterKnife+Retrofit+Picasso+RecycleView 实现一个小案例) 分享了在使使用 RecycleView 的时候如何结合 ButterKnife、Retrofit、Picasso 等框架进行使用。现在来聊聊 MVC 以及 MVP 框架吧。
下面来看看 MVC 的介绍:
MVC简介
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
其中 M 层处理数据,业务逻辑等;V 层处理界面的显示结果;C 层起到桥梁的作用,来控制 V 层和 M 层通信以此来达到分离视图显示和业务逻辑层。
Android 中的 MVC
- 视图层(View)
一般采用 XML 文件进行界面的描述,这些 XML 可以理解为 AndroidApp 的 View。使用的时候可以非常方便的引入。同时便于后期界面的修改。逻辑中与界面对应的 id 不变化则代码不用修改,大大增强了代码的可维护性。
- 控制层(Controller)
Android 的控制层的重任通常落在了众多的 Activity 的肩上。这句话也就暗含了不要在 Activity 中写代码,要通过 Activity 交割 Model 业务逻辑层处理,这样做的另外一个原因是 Android 中的 Activity 的响应时间是 5s,如果耗时的操作放在这里,程序就很容易被回收掉。
- 模型层(Model)
我们针对业务模型,建立的数据结构和相关的类,就可以理解为 AndroidApp 的 Model,Model 是与 View 无关,而与业务相关的。对数据库的操作、对网络等的操作都应该在 Model 里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。
MVC MVP 对比
通过分析上图,只需知道 MVC 传统模式是没有把 View 和 Model 层隔离开的,MVP 模式则是 View 层和 Model 完全解耦开,通过 Presenter 这个中间人进行传递信息。
下面看看 MVP 的介绍:
MVP
View:负责绘制 UI 元素、与用户进行交互(在 Android 中体现为 Activity)
Model:负责存储、检索、操纵数据(有时也实现一个 Model interface 用来降低耦合)
Presenter:作为 View 与 Model 交互的中间纽带,处理与用户交互的负责逻辑。
View interface:需要 View 实现的接口,View 通过 View interface 与 Presenter 进行交互,降低耦合,方便进行单元测试
一句话解释就是:Presenter 是 View 和 Model 之间的代理。
MVP 优点
- 降低耦合度,实现了 Model 和 View 真正的完全分离,可以修改 View 而不影响 Modle
- 模块职责划分明显,层次清晰
- 隐藏数据
- Presenter 可以复用
- 利于测试驱动开发
- View 可以进行组件化
- 代码灵活性
MVP缺点
Presenter 中除了应用逻辑以外,还有大量的 View->Model,Model->View 的手动同步逻辑,造成 Presenter 比较笨重,维护起来会比较困难。
由于对视图的渲染放在了 Presenter 中,所以视图和 Presenter 的交互会过于频繁。
如果 Presenter 过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么 Presenter 也需要变更了。
额外的代码复杂度及学习成本。
代码实现:
MVP 是一种思想,每个人的理解不一样,所以每个人的实现都是大同小异的。谷歌推出官方的 MVP Demo,我们可以参考谷歌给出的进行稍微修改一点点,形成自己风格的 mvp 模式。注意,模式的实现是没有所谓的标准的,只要达到这种解耦效果就可以了。
这里以 手机助手 的【推荐】栏目举例说明
模块解析:
Contract 接口:里面定义 presenter 接口 和 view 接口
presenter :负责和 view,module 交互
view :基本都是对控件进行更新即可
BaseView
public interface BaseView {
//声明公共的一些方法
void showLodading();//显示加载进度条
void dimissLoading();//关闭加载进度条
}
RecommendContract
该类存放两个接口,View 接口和 Presenter 接口。
以前两个接口都是分开写的,现在合起来放在一个接口类里面,显得不那么凌乱了。接口 View 给具体视图层实现,譬如本例中的 RecommendFragment
接口 Presenter 给具体的 Presenter 层实现,譬如本例的 RecommendPresenter
public interface RecommendContract {
//接口与接口之间的继承是用 extends
interface View extends BaseView{
void showResult(List<AppInfo> datas); //显示数据
void showNodata(); //提示没数据
void showError(String msg); //提示错误
}
// BasePresenter 暂时为空,就不列代码出来了,以后可以增加
interface Presenter extends BasePresenter{
public void requestDatas();//请求数据
}
}
Model 层
RecommendModel 类里面调用到的 HttpManager、ApiService 类的代码就不贴出来了,因为本例主要讲 MVP 模式。
public class RecommendModel {
// presenter 层调用该方法,执行完毕有回调方法
public void getApps(Callback<PageBean<AppInfo>> callback){
HttpManager manager = new HttpManager();
ApiService apiService =manager.getRetrofit(manager.getOkHttpClient()).create(ApiService.class);
apiService.getApps("{'page':0}").enqueue(callback);
}
}
Presenter 层
实现 Contract 层接口 RecommendContract.Presenter,实现该接口下的抽象方法。
实现抽象方法:requestDatas(),请求数据
代码中 引用 model 层的对象RecommendModel mModel,
通过 mModel.getApps() 调用 Model 层的具体方法实现需求。引用 view 接口实例对象,接口引用指向一个对象 RecommendContract.View mView,
通过 mView.showLodading()、mView.showNodata() 等调用 View 层具体方法实现需求。
public class RecommendPresenter implements RecommendContract.Presenter {
//引用 model 层的对象
private RecommendModel mModel;
//引用 view 接口实例对象,接口引用指向一个对象
private RecommendContract.View mView;
//构造方法中传过来 view 对象
public RecommendPresenter(RecommendContract.View view){
this.mView = view;
mModel = new RecommendModel();
}
//实现 RecommendContract.Presenter 接口,重写接口的抽象方法
@Override
public void requestDatas() {
//调用 实现了RecommendContract.View 接口的 fragment 里面重写的 showLodading()方法
mView.showLodading();
mModel.getApps(new Callback<PageBean<AppInfo>>() {
@Override
public void onResponse(Call<PageBean<AppInfo>> call, Response<PageBean<AppInfo>> response) {
if(response !=null){
mView.showResult(response.body().getDatas());
}
else{
mView.showNodata();
}
mView.dimissLoading();
}
@Override
public void onFailure(Call<PageBean<AppInfo>> call, Throwable t) {
mView.dimissLoading();
mView.showError(t.getMessage());
}
});
}
}
View 层
实现 Contract 层接口 RecommendContract.View,实现该接口下的几个抽象方法:
showLodading(),
dimissLoading(), showNodata(), showError(), showResult()代码中使用 RecommendContract.Presenter mPresenter 接口实例对象,接口引用指向一个对象,
通过 mPresenter.requestDatas() 调用 Presenter 的 requestDatas()方法。
以此达到解耦的目的。
public class RecommendFragment extends Fragment implements RecommendContract.View {
@BindView(R.id.recycle_view)
RecyclerView mRecyclerView;
private RecomendAppAdatper mAdatper;
private ProgressDialog mProgressDialog;
private RecommendContract.Presenter mPresenter;//presenter接口实例对象,接口引用指向一个对象
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_recomend, container, false);
ButterKnife.bind(this, view);
mProgressDialog = new ProgressDialog(getActivity());
//实例化 Presenter
mPresenter = new RecommendPresenter(this);
initData();
return view;
}
private void initData(){
//调用 presenter 去请求数据,实际上,presenter 是指挥 Model 去做实际操作
mPresenter.requestDatas();
}
//数据显示
private void initRecycleView(List<AppInfo> datas){
//为RecyclerView设置布局管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
//为RecyclerView设置分割线(这个可以对DividerItemDecoration进行修改,自定义)
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
//动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdatper = new RecomendAppAdatper(getActivity(),datas);
mRecyclerView.setAdapter(mAdatper);
}
// 实现 RecommendContract.View 接口后 重写的抽象方法
@Override
public void showResult(List<AppInfo> datas) {
initRecycleView( datas);
}
@Override
public void showNodata() {
Toast.makeText(getActivity(),"暂时无数据,请吃完饭再来",Toast.LENGTH_LONG).show();
}
@Override
public void showError(String msg) {
Toast.makeText(getActivity(),"服务器开小差了:"+msg,Toast.LENGTH_LONG).show();
}
@Override
public void showLodading() {
mProgressDialog.show();
}
@Override
public void dimissLoading() {
if(mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
}
}
总结
通过上面代码分析可以很清晰的明白 MVP 框架模式是怎么进行代码解耦的。下面再次梳理一下 MVP 实现步骤:
定义 Presenter、View 接口(可以向上面例子,放在 Contract 接口里面):接口里面定义 presenter 层、view 层的抽象方法。简单的说就具体实现类所要实现的方法。
具体 Presenter 层实现类实现定义的 Presenter 接口,实现该接口的抽象方法。
比如 RecommendPresenter 类实现了 RecommendContract.Presenter 接口,实现 requestDatas() 方法。具体 View 层实现类实现定义的 View 接口,实现该接口下的抽象方法。
比如 RecommendFragment 类实现了 RecommendContract.View 接口,实现了 dimissLoading(), showNodata(), showError(), showResult() 等方法。RecommendFragment 通过类中的 RecommendPresenter 对象调用 Presenter 层里面的方法:requestDatas() 方法。在 Presenter 层中通过 Model 对象调用 Model 层里面的具体方法:getApps(). Model 层的方法执行完后通过回调把结果回调给 Presenter 层,Presenter 层再通过 View 层的接口实例对象,调用相关方法把结果回调到具体实现了该 View 接口的 View 层。
欢迎关注我的微信公众号: