大概在今年二月的时候google估计是看不下去各种奇葩的所谓AndroidMVP架构了吧,干脆自己写了个官方指导,名字取得很大气blueprint!蓝图!
例子使用MVP的架构实现一个todo list这样简单的app,虽然同是使用MVP,但是给出了四个使用MVP结合其他的架构的实现方式:
1.todo-mvp 一个最简单的基本的MVP
2.todo-mvp-loaders 基于MVP,但是数据获取使用了Loader,Loader我个人没用过,但是相信大部分人都用别的方式实现过,我觉得并没有什么神奇的
3.todo-mvp-databinding 基于MVP,但加了DataBinding,没了解过的,可以看看官方文档,我还挺喜欢这个东东,省了好多废话的代码啊,又清晰又精简,像findViewById这样的屎一样的代码都不需要再写了,只是,估计踩坑了会比较难debug吧
4.todo-mvp-clean 基于MVP,但结合了Clean Architecture的架构思想,不清楚的可以看看这位仁兄的理解,当然也可以看看Bob大叔(貌似是架构提出者)的解释,这个具体后面再扯
在谈具体的代码和实现前,先问自己这样一个问题,为啥要有架构呢?
谈谈我浅显的两点理解,
第一,职责单一,角色各司其职,像一个在井井有条的制度下运行的王朝,而不是代码写到后来乱成一团,除了问题不知道该改哪里,扩展功能的时候根本不知道该往哪里加;
第二,可测性,虽然后面这点对于大多数人来说根本不在乎,因为真的没几个移动应用实践了TDD的,但是可测性会像一个框,框住你写的代码,职责单一,依赖解耦什么的,你被框着写,没法违反规则,代码自然好,而烂代码根本无法测试。Google给出的这个实例代码,对View,Model,Presenter每一层都进行了详尽的测试,并结合了不同的测试方法,有兴趣的读者可以看看这篇文章,作者剖析了例子中的测试部分,讲得很好。
接下来我就对我看过的四个工程一一做分析:
先从这四个工程的第一个讲起吧,这个例子向我们展示了,官方示例对MVP各自职责的分配,以及各种各样的androidMVP中fragment&Activity扮演什么样的角色。先盗个官方图:
在这个例子中Fragment扮演了view的角色,Activity则是一个全局controller,串联起Fragment作为View和Presenter建立服务关系。
看这个包结构,每个包其实就是一块业务,从上往下分别是统计页面,任务详情,任务列表。
大致都有一个Activity,Fragment,Presenter以及一个Contract
举一个task列表的例子来说会比较清晰:
TasksContract:大家都知道在P和V的眼里,看到对方都是接口,所以其实这个类就是定义P和V各自遵循的接口
public interface TasksContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showTasks(List<Task> tasks);
void showAddTask();
void showTaskDetailsUi(String taskId);
void showTaskMarkedComplete();
void showTaskMarkedActive();
void showCompletedTasksCleared();
void showLoadingTasksError();
void showNoTasks();
... }
interface Presenter extends BasePresenter {
void result(int requestCode, int resultCode);
void loadTasks(boolean forceUpdate);
void addNewTask();
void openTaskDetails(@NonNull Task requestedTask);
void completeTask(@NonNull Task completedTask);
void activateTask(@NonNull Task activeTask);
void clearCompletedTasks();
void setFiltering(TasksFilterType requestType);
TasksFilterType getFiltering(); }
}```
而TasksFragment实现了View的接口
```java
public class TasksFragment extends Fragment implements TasksContract.View ```
TasksPresenter实现了Presenter的接口
```java
public class TasksPresenter implements TasksContract.Presenter```
TasksActivity则是一个大的controller,创建了fragment和presenter,并使得两者联系在一起
```java
tasksFragment = TasksFragment.newInstance();ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
presenter的构造中,又让view认识了presenter
mTasksView.setPresenter(this);```
那么model是谁呢?
TasksRepository,实现TasksDataSource接口,所以别人看到的model也是一个接口
就这么简单,我觉得这个架构很清晰,比有些什么把Fragment和Activity当成presenter的好理解得多,其他的MVP架构中presenter很容易出现onXXX命名的方法,但其实我认为这是不应该的,presenter不应该搅和到生命周期中去让别人理解,它应该是脱离android的,就像例子中的start方法,具体做什么就叫什么,除了V部分,M和P都应该是脱离android可测的,这才是合理的设计。
比如我要添加一个show task list的功能,我就很明白,V要有showList接口,M中要有getList接口如果是异步的应该还有callback,P中要有loadList接口,各司其职,这样就很简单也很好扩展,再复杂的业务,也是这样去搭建。
>[todo-mvp-loaders](https://github.com/googlesamples/android-architecture/tree/todo-mvp-loaders/)
我只谈谈这个例子和第一个差别。唯一的区别一个是callback从Model拿取结果,另一个是使用loader从Model拿取结果,我觉得意义不是很大吧,跳过这个。
>[todo-databinding](https://github.com/googlesamples/android-architecture/tree/todo-databinding/)
这个就比较有趣了,dataBinding是一个扩展包,最低可以支持到2.1,这里我只侧重对MVP各个职能的影响和分布,先盗个官图
![mvp-databinding](http://upload-images.jianshu.io/upload_images/1124312-51b2d46230f5de90.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
MVP角色都没有变,只是会发现这里多了一个ViewModel的角色,我个人觉得ViewModel对于那种UI的呈现需要对model里的数据做一些加工才能用来呈现的场景再适合不过了,这个demo里面VM的作用就是对mTaskListSize做一些加工,处理为空的场景,以及对filter在UI上的文字设定等的影响,这个demo里面用VM是因为databinding的场景如果有VM配套就非常得得心应手,好像有了一个专门服务UI的model,只要绑定这个mode的相关属性的改变,就可以很容易刷新UI什么的。
下面这段代码演示了在V中绑定VM已及Presenter,如果想了解更具体的就请自己去看DataBinding的语法吧。
```java
TasksFragBinding tasksFragBinding = TasksFragBinding.inflate(inflater, container, false);
tasksFragBinding.setTasks(mTasksViewModel);
tasksFragBinding.setActionHandler(mPresenter);
这个例子,是MVP与CleanArchitecture的结合。先来扒一扒CleanArchitecture是个啥思想,一个洋葱图说清真相:
乍看起来灰常复杂,其实作者的意思就一点
The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.
中文解释就是向内依赖,不可以反过来,里面角色的不知道外面的角色,这就是必须遵循的依赖法则,就像“欲练此功必先自宫”一样。
它的角色分配是怎样呢?
我谈谈我的理解吧,如果理解有错误还请各位指正。
1.最里面Entities,是通用的业务逻辑,比如你公司做IP电话的,那么IP电话的拨打,接通,多方通话,这些,不管你做哪个平台,都是一样的。当然如果你就是个简单的app,那么你可以理解为就是你的 business objects
2.再往外UseCases,其实就是用例,是说针对你这个App,拨出电话,是一个用例,接听电话是另一个用例,如此这般,用例其实就是一个个功能,比如我们这个todoList,DeleteTask,SaveTask,都是用例,它可以为不同的Presenter所共用,相信你应该有Get到咯。
3.再外就是Presenter,Controller之流啦,他们负责沟通,沟通啥咧?自然是里面和外面咯,他们在UseCase和最外圈的ExternalInterface之间沟通,比如我们的Presenter沟通了UI和UseCase。所以作者把他们叫做Interface Adapters,其实就是一个Adapter,把数据按照UI需要的方式组织起来传递给UI,把UI的对数据的改变传递给最外圈。
4.最外圈ExternalInterface的概念非常的广泛,UI,DB,Device,Web都算。
那么回到我们研究的这个demo本身,它和单纯的MVP有何不同呢?
还是拿task list这个业务来说,TasksPresenter不再找Model,而是找GetTask,CompleteTask,ActivateTask,ClearCompleteTasks这几个UseCase,而UseCase才去找Model,所以P不找M啦,而是通过Usecase调戏M。这下通过看UseCase,你就可以清晰的知晓你到底有哪些业务,并可以针对每个测试。
public class GetTasks extends UseCase<GetTasks.RequestValues, GetTasks.ResponseValue>```
补个图,让你更清晰,其实就是有了一个业务层,对于负责的业务,这比单纯的MVP能更好理清业务处理。
![mvp-clean](http://upload-images.jianshu.io/upload_images/1124312-e9cb34b9371dd58b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我水平有限,就写到这里吧,欢迎各位高手批评指正交流心得~我写的比较简单,最好是自己去看了源码以及DataBinding,CleanArchitecture才会更加理解,关看我的文字可能会比较云里雾里。