MVP在Android中简易易懂的实现案例
前言
最进想做了一个新的小项目,总想来点对自己来说新鲜的东西。后面看到Google老大推荐使用MVP架构,顿时觉得不试试看都不好说是羡慕它的小弟了。良心大哥也专门在Github推出了一个项目Android Architecture Blueprints,用来展示Android用各种的MVP框架,也能算了官网教程了。在网络收集资料看了一下大神们的分析后,自己动手试了试。那么就马上上手来看一看MVP究竟如何。(本文都是围绕项目中todo-mvp部分来介绍
)
MVP模式
先上一张图,然后根据实际的代码我们来讲讲Google是怎么看待MVP架构的
基类
首先丁一两个Base接口,分别是作为Presenter和View的基类
public interface BaseView<T> {
// 为View设置Presenter
void setPresenter(T presenter);
// 初始化界面控件
void initView(View view);
}
public interface BasePresenter {
// 获取数据并改变界面显示,在todo-mvp的项目中的调用时机为Fragment的OnResume()方法中
void start();
}
契约类
官方事例中加如契约类来统一管理View和Presenter。这样整个功能可以在契约类一目了然。老大就是老大,这一点还是很厉害的。事例如下:
public interface YourContract {
interface View extends BaseView<Presenter>{
//这里加View功能方法
void showError();
void showLoading();
void Stoploading();
}
interface Presenter extends BasePresenter{
// 同上
void loatPosts(int PagerNum,boolean cleaing);
void reflush();
void loadMore(int PagerNum);
}
}
对了,其中BaseView中含方法setPresenter,该方法作用是在将presenter实例传入view中,其调用时机是presenter实现类的构造函数中。如下
public YourPresenter(Context context, YourContract.View view) {
this.context=context;
this.view=view;
this.view.setPresenter(this);
}
Activity的作用
在讲这个之前,不知道大家有没有注意到上没图中,Fragment是作为View层而Activity是作为Presenter的,有没有想过Google为什么要推荐这样做呢?
MVC中Activity的作用
按照我们之前的习惯或者说在MVC模式中,Activity是作为View层和用户打交道,接收用户数据的输入和输出的。特别是我们会在Activity的声明周期中写入一些逻辑来实现我们想要的效果。这样很方便,但是后果是我们的Activity特别的臃肿,想一想如果我们一些通用的功能每个Activity里都要写一次不是一件很痛苦的事情。
这个时候,Activity 不仅承担了 View 的角色,还承担了一部分的 Controller 角色,这样一来 V 和 C 就耦合在一起了,虽然这样写方便,但是如果业务调整的话,要维护起来就难了,而且在一个臃肿的 Activity 类查找业务逻辑的代码也会非常蛋疼,所以看起来有必要在 Activity 中,把 View 和 Controller 抽离开来,而这就是 MVP 模式的工作了。
MVP中View层的实现
至于为什么要选择Fragment作为View层的实现类。我看到网上有这两种说法,第一个原因是我们把activity作为一个全局控制类来创建对象,把fragment作为view,这样两者就能各司其职。第二个原因是因为fragment比较灵活,能够方便的处理界面适配的问题
MVP 把 Activity 中的 UI 逻辑抽象成 View 接口,把业务逻辑抽象成 Presenter 接口,Model 类还是原来的 Model。
来看一段代码就能知道Activity的作用了(毕竟代码能够一目了然):
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
if (savedInstanceState!=null){
mainFragment= (MainFragment) getSupportFragmentManager().getFragment(savedInstanceState,"MainFragment");
bookmarksfragment=(BookmarksFragment) getSupportFragmentManager().getFragment(savedInstanceState,"BookmarksFragment");
}else {
mainFragment=MainFragment.newInstance();
bookmarksfragment=BookmarksFragment.newInstance();
}
new BookmarksPresenter(MainActivity.this,bookmarksfragment);
}
看了上面的代码大概就能知道Activity的作用是什么了。主要是作为全局的控制,负责创建View以及Presenter实例,并将二者联系起来。
View层的实现
public class YourFragment extends Fragment implements YourContract.View {
private YourContract.Presenter presenter;
@Override
public void onResume() {
super.onResume();
presenter.start();
}
@Override
public void setPresenter(YourContract.Presenter presenter) {
if (presenter!=null){
this.presenter=presenter;
}
}
@Override
public void showError() {
Snackbar.make(fab, R.string.loaded_failed,Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.retry, new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.reflush();
}
})
.show();
}
@Override
public void showLoading() {
refresh.post(new Runnable() {
@Override
public void run() {
refresh.setRefreshing(true);
}
});
}
@Override
public void Stoploading() {
refresh.post(new Runnable() {
@Override
public void run() {
refresh.setRefreshing(false);
}
});
}
}
可以看到通过setPresenter方法获取到Presenter的实例。然后在Fragment的生命周期中调用presenter.start()方法。这样View层只负责数据给用户呈现他们看到的东西,而不去管具体是怎样实现的(ps:我觉得在onResume()方法之前调用应该都行吧,不知道是不是对的。如果有错,还请指教)
Presenter层的实现
public class YourPresenter implements YourContract.Presenter {
public YourPresenter(Context context, YourContract.View view) {
this.context=context;
this.view=view;
this.view.setPresenter(this);
}
@Override
public void loatPosts(int PagerNum, final boolean cleaing) {
//具体实现就不贴了,有点长
}
@Override
public void start() {
loatPosts(CurrentPagerNum,true);
}
@Override
public void reflush() {
loatPosts(CurrentPagerNum,true);
}
@Override
public void loadMore(int PagerNum) {
loatPosts(CurrentPagerNum+PagerNum,false);
}
}
在构造方法中,Presenter将自身的事例传递给了View,这样View就能调用Presenter层的方法来处理业务逻辑了。在start()方法中,处理了数据加载。
Model层的实现
项目中model层最大的特点是被赋予了数据获取的职责,与我们平常model层只定义实体对象截然不同,实例中,数据的获取、存储、数据状态变化都是model层的任务,Presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样model、presenter、view都只处理各自的任务,此种实现确实是单一职责最好的诠释。
总结
MVP的好处
- 分离了视图逻辑和业务逻辑,降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle
- Activity 只处理生命周期的任务,代码变得更加简洁
- 视图逻辑和业务逻辑分别抽象到了 View 和 Presenter 的接口中去,提高代码的可阅读性
- View可以进行组件化。在MVP当中,View不依赖Model。这样就可以让View从特定的业务场景中脱离出来,可以说View可以做到对业务完全无知。它只需要提供一系列接口提供给上层操作。这样就可以做到高度可复用的View组件。
- 利于测试驱动开发。Presenter 被抽象成接口,可以有多种具体的实现,所以方便进行单元测试。在使用MVP的项目中Presenter对View是通过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。可以通过Mock一个View对象,这个对象只需要实现了View的接口即可。然后依赖注入到Presenter中,单元测试的时候就可以完整的测试Presenter应用逻辑的正确性。
以上就是我这段时间来对MVP的理解了。如果有错误的地方,欢迎指教哦。