总结梳理下 Android 架构设计的几种模式
 
为什么我们的项目需要使用架构模式?个人理解使用架构模式是为了使我们的代码模块化,达到模块内部高内聚和模块间低耦合的目的,同时开发人员只需要关注相应的业务逻辑点,提高开发效率。当然,使用架构模式不一定是减少了代码量,相反还可能增加代码量;但是它帮你简化了逻辑,提高了扩展性和兼容性。针对不同的项目规模,合理采用或不采用架构模式都是值得考虑的。
MVC MVC(Model-View-Controller)
 
Model: 模型层。主要是实体类,数据库,网络等存在的层面,Model 将新的数据发送到 View 层,用户得到数据响应。 
View: 视图层。一般以 xml 为代表的视图界面,它显示来自 Model 层的数据,同时将用户的点击操作等事件传送到 Controller 层。 
Controller 控制层。一般以 Activity/Fragment 为代表,它连接 Model 层和 View 层;它收到 View 层发送过来的事件请求,然后从 Model 层获取数据,并展示给 View 层。 
 
 
优点 多视图可共用同一模型,重用性高;模块彼此独立清晰,耦合低。
缺点 增加了实现复杂性;视图与控制层联系过于紧密,没有完全解耦;视图层无法组件化,复用比较困难。
MVP Model(Model View Presenter),Model: 逻辑模型,View: 视图模型,Presenter: 接口。
 
MVP 是 MVC 的升级版,让 Model 层和 View 层完全解耦,代码清晰。
MVP 中的 P,是 Presenter 的含义,和 MVC 比较类似,都是将用户对 View 的操作交付给 Presenter,Presenter 会执行相应的逻辑,在这过程中会操作 Model,当 Model 执行完业务逻辑之后,同样是通过观察者模式把自己的结果传递出去,不过不是告诉 View,而是告诉 Presenter,Presenter 得到消息后,通过 View 的接口更新页面。
这里详细代码可查看源码:AndroidArchitDemo 
MVP 代码演示  
如图所示,代码结构分为三个模块,每个模块内封装了对应的基类。实际使用时,每个页面都会有一个对应的合约(Contract) 类,里面包含该页面的 View 接口和 Presenter 接口。
比如 userdetail 功能页面,里面就有对应的 合约接口类,页面 UI 类以及 Presenter 实现类。这样做结构会比较清晰,当然每新建一个页面都有对应的合约及 Presenter 类,也是比较麻烦。
优点 Model 层和 View 层完成接耦;View 层可以组件化,便于单元测试。
缺点 接口类增多,实现复杂;View 层和 Presenter 层通过接口交互,一旦 View 层某个 UI 元素修改,需要修改接口,则所有接口事件均需要修改
MVVM  
Model: 模型层,负责数据实现和逻辑处理,类似 MVP。 
View: 视图层,类似 MVP 
ViewModel:创建关联,将 Model 层和 View 层绑定起来,实现双向绑定。 
 
也就是说,MVVM 把 View 和 Model 的同步逻辑自动化了。以前 Presenter 负责的 View 和 Model 同步不再手动地进行操作,而是交由框架所提供的 Binder 进行负责。只需要告诉 Binder,View 显示的数据对应的是 Model 哪一部分即可。
Android 官方推出的 MVVM 的 DataBinding,便是一个双向绑定的库。
MVVM 代码演示 启用:build.gradle 中 开启
1 2 3 4 5 6 7 android {     ...          dataBinding {         enabled true     } } 
 
Model 层定义相关的 bean 类以及数据获取逻辑,这块没什么特别之处,这里不特殊说明。
View 层即界面布局 xml 和 Activity 中引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto">     <data>         <variable             name="viewModel" type="com.bashellwang.androidarchitdemo.mvvm.viewmodel.SchoolViewModel" />     </data>     <android.support.constraint.ConstraintLayout         android:layout_width="match_parent"         android:layout_height="match_parent">         <TextView             android:layout_marginTop="50dp"             android:id="@+id/tv_name"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="@{viewModel.mSchool.toString()}"             android:textColor="#000000"             android:textSize="16sp"             app:layout_constraintLeft_toLeftOf="parent"             app:layout_constraintRight_toRightOf="parent"             app:layout_constraintTop_toTopOf="parent" />         <TextView             android:id="@+id/tv_desc"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="@{viewModel.mSchool.description}"             android:textColor="#000000"             android:layout_marginTop="20dp"             android:textSize="16sp"             app:layout_constraintLeft_toLeftOf="parent"             app:layout_constraintRight_toRightOf="parent"             app:layout_constraintTop_toBottomOf="@+id/tv_name" />         <Button             android:id="@+id/btn_get"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="随机查询学校"             android:onClick="@{viewModel.onItemClick}"             app:layout_constraintLeft_toLeftOf="parent"             app:layout_constraintRight_toRightOf="parent"             app:layout_constraintTop_toBottomOf="@+id/tv_desc" />     </android.support.constraint.ConstraintLayout> </layout> 
 
1 2 3 4 5 6 7 8 9 10 public class MvvmDemoActivity extends AppCompatActivity {     @Override     protected void onCreate(@Nullable Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         ActivityMvvmDemoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm_demo);         SchoolViewModel viewModel = new SchoolViewModel(binding);     } } 
 
可以看到 View 层只处理 UI 相关的逻辑。
ViewModel 层处理业务相关逻辑,这里继承 BaseObservable,当获取到新数据后调用 notifyChange 即可。因为数据和 UI 已经绑定了,所以调用后相应的控件会自动更新 UI。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class SchoolViewModel extends BaseObservable {     private static final String TAG = "SchoolViewModel";     public School mSchool;     private ActivityMvvmDemoBinding mBinding;     private SchoolModelDao mSchoolDao;     public SchoolViewModel(ActivityMvvmDemoBinding demoBinding) {         this.mBinding = demoBinding;         // 把自己和 binding 绑定,实现双向绑定         mBinding.setViewModel(this);         this.mSchoolDao = new SchoolModelDao();         init();     }     // 初始化数据展示     public void init() {         mSchool = new School(5, "aa", "aaa", 300);     }     public void onItemClick(View view) {         mSchoolDao.getSchoolById(new Random().nextInt(10), new Callback<School, String>() {             @Override             public void onSuccess(School school) {                 Log.e(TAG, "获取新school 成功:" + school.toString());                 updateData(school);             }             @Override             public void onFail(String s) {             }         });     }     private void updateData(School school) {         mSchool = school;         notifyChange();     } } 
 
优点 提高可维护性。解决了 MVP 大量的手动 View 和 Model 同步的问题,提供双向绑定机制。提高了代码的可维护性。
简化测试。因为同步逻辑是交由 Binder 做的,View 跟着 Model 同时变更,所以只需要保证 Model 的正确性,View 就正确。大大减少了对 View 同步更新的测试。
缺点 过于简单的图形界面不适用,或说牛刀杀鸡。
对于大型的图形应用程序,视图状态较多,ViewModel 的构建和维护的成本都会比较高。
数据绑定的声明是指令式地写在 View 的模版当中的,这些内容是没办法去打断点 debug 的。
代码下载 本文相关 demo 源码可见:AndroidArchitDemo 
参考资料 
googlesamples/android-architecture 
 
todo-mvp 
 
从0到1搭建MVP框架 
 
深入了解MV**模式 
 
一步步带你精通MVP 
 
Android 架构设计 — 关于 MVVM 模式的探讨 
 
Android MVP架构从入门到精通-真枪实弹