MVC和MVP简单对比
MVC
MVC要加载数据时,一般来说vClass(通常是Activity)如下
public class VClass extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
}
private void loadData(){
XxxBiz.loadData(new XxxCallback()){
void onSuccess(T t){
// 加载完成后的动作
}
};
}
}
虽然请求数据的具体实现交由XxxBiz了,但是Activity还是申明有关请求数据的方法,也建立了XxxCallBack回调类对象。并没有做到完美的分离,再看看MVP如何做的。
MVP
MVP要加载数据时,通常是V和P协调动作。
public class V extends Activity implements AbsV{
private P p;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
p.loadData();
}
public void update(T data){
// bind ui
}
}
public class P implements AbsP{
private V v;
public void loadData(){
// loading
T data = ... ;
v.update(data);
}
}
由代码可以知道,MVP加载数据时,V只需要让P去加载数据,完成后由P通知V修改UI。
这样子V中无需申明加载数据方法,无需建立加载数据回调对象,做到完全不关心加载数据的事件。
代码分离程度高,V和P只关心自己的事情。
先从了解谷歌官方推荐的几种模式。
Clean模式
Clean模式下,当Activity/Fragment销毁时,Presenter也会被销毁,在onCreate()重建时重新被创建。可以利用onSaveInstanceState()保存些信息,在重建时获取并设置,但是想要保存对后台线程的引用就很难了。
这是官方的架构图,比较前的架构,新增的这个Domain Layer,用来隔离Presentation Layer和Data Layer,减轻Presentation的负担,明显感受就是代码量的减少。新架构下,Presentation无需进行具体的操作,业务逻辑几种在Domain Layer这层,Presentation 执行调用和结果返回处理。
- Presentation Layer:该层囊括了 View 和 Presenter。Activity/Fragment属于VIEW层,负责展示图形界面和填充数据;Presenter就是PRESENTER层,负责Domaim Layer业务逻辑的调起和返回数据的接受,并将数据传递给VIEW层,也可以反向过来。
- Domain Layer:负责实现app的业务逻辑,该层中由普通的Java对象组成,一般包括 Usecases 和 Business Logic。
- Data Layer:负责提供数据,这里采用了 Repository 模式。Domain 只需告诉仓库管理员需求,由仓库管理员查找并递交,Domain无需知道东西的存放位置。
Demo目录
在分析代码 之前,先进行简单的了解。
从外层看起
BasePresenter、BaseView
View和Presenter动作的抽象,是MVP模式中所有V和P需要实现的接口。
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter
最为V的AddEditTaskFragment
实现了BaseView,最为P的AddEditTaskPresenter
实现了BasePresenter。但中间多了一层接口AddEditTaskContract
,再次接口申明AddEditTask任务中V和P的动作方法。
UseCase
UseCase是任务的抽象,xxxTask是任务的具体实现。如DeleteTask
,输入Domain层。
public class DeleteTask extends UseCase<DeleteTask.RequestValues, DeleteTask.ResponseValue>
UseCaseHandler、UseCaseScheduler、UseCaseThreadPoolScheduler可以完成一个任务的执行流程。
操作交互流程
以addedittask任务模块为参考,以一个点击事件为例。
- V层
AddEditTaskFragment
中的某个View触发点击事件,调用所持有的P层对象的方法。 - P层
AddEditTaskPresenter
对象方法被调用,借助Domain层UseCaseHandler
对象进行UseCase业务处理。 - UseCaseHandler:任务的执行者,项目中对
UseCase
设置taskId、callback,并调起UseCaseScheduler.execute()
- UseCaseScheduler:方案调度器,实现类
UseCaseThreadPoolScheduler
。调度器执行后,会在建立的Runnable
中执行UseCase.run()
- TasksRepository:任务库。在此案例中
UseCase.run()
–>UseCase.executeUseCase()
–>TasksRepository.getTask()
调起任务库的查询任务,参数有TaskId,CallBack。 - TasksDataSource:任务数据源。实现类
FakeTasksRemoteDataSource
、TasksLocalDataSource
。任务库查询TasksRepository.getTask()
会调起数据源TasksDataSource.getTasks()
,此方法进行数据源进行Cursor游标查询或是API请求等耗时操作。所以任务执行起点UseCase.run()
不能在UI线程中执行
总结
架构的作用是:把复杂的系统变成高内聚低耦合,应付几十个人能同时协同作战时能够有序稳定,相对有节奏的开发。
但对于Android App层,2、3个能力差不多的人就能形成一个高效的整体,不存在需要应付几十个人同时协同作战的情景。
并无需过于纠结框架的优秀性,应该把重点放在敏捷开发,高效迭代上,不能为了框架而框架。
- 过于复杂的框架设计无法适应越来越快速的产品演进,同时增加了团队学习成本
- Android 本身就是 MVC 框架,开发中保持代码的简洁易懂,适度重构
- 合适的才是最好的,非必要无需设计复杂的框架
实际开发重点
1. 保证App的敏捷开发、快速迭代,就是高效的开发模式了
2. 拆解业务,对功能进行合理划分抽象,拆分library
3. 良好的接口和模块设计,而非无尽的继承
4. 保证代码简洁,易懂,一目了然
最后,Android 项目组件化是一个很好的方案。