本文为菜鸟窝作者 吴威龙 连载
菜鸟窝是专业的程序猿在线学习平台,提供最系统的 Android 项目实战课程
如需转载,请联系菜鸟窝公众号(cniao5),并注明出处。
前言
上一篇文章
RecycleView 综合使用案例(结合 ButterKnife、Retrofit、Picasso) 分享了在使使用 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 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> 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>() {
@Override
public void onResponse(Call> call, Response> response) {
if(response !=null){
mView.showResult(response.body().getDatas());
}
else{
mView.showNodata();
}
mView.dimissLoading();
}
@Override
public void onFailure(Call> 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 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 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.RecommendFragment 通过类中的 RecommendPresenter 对象调用 Presenter 层里面的方法:requestDatas() 方法。在 Presenter 层中通过 Model 对象调用 Model 层里面的具体方法:getApps(). Model 层的方法执行完后通过回调把结果回调给 Presenter 层,Presenter 层再通过 View 层的接口实例对象,调用相关方法把结果回调到具体实现了该 View 接口的 View 层。
撸这个项目的一半,你就是大神 , 戳http://mp.weixin.qq.com/s/ZagocTlDfxZpC2IjUSFhHg