也看AndriodMVP官方最佳实践

大概在今年二月的时候google估计是看不下去各种奇葩的所谓AndroidMVP架构了吧,干脆自己写了个官方指导,名字取得很大气blueprint!蓝图!

https://github.com/googlesamples/android-architecture

例子使用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每一层都进行了详尽的测试,并结合了不同的测试方法,有兴趣的读者可以看看这篇文章,作者剖析了例子中的测试部分,讲得很好。

接下来我就对我看过的四个工程一一做分析:

todo-mvp/

先从这四个工程的第一个讲起吧,这个例子向我们展示了,官方示例对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);

todo-mvp-clean

这个例子,是MVP与CleanArchitecture的结合。先来扒一扒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才会更加理解,关看我的文字可能会比较云里雾里。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容