本文俩个任务:
MVP是对MVC的C的演化,MVVM是对MVP的P的演化。而Android领域的MVVM自身也进行了一次演化,即从2015年DataBinding推出开始,由传统的MVVM到2017谷歌推出了AAC标准架构。并在二者迭代的过程中,也出现了基于传统的DataBinding增强的方案(MVVM Light Toolkit使用指南)。
MVVM是一种思想。即UI随数据更改而更改。它独特的地方就在于它的DataBinding特性。但请注意,虽然Google2015专门发布了一个库叫做DataBinding,但是这里说的DataBinding并不是指的某个具体的库,而是指的一种“行为”,指的是一类的”将数据Model映射到View”的框架。譬如老的DataBing库,新AAC的LiveData。
View: 对应于Activity和XML,负责View的绘制以及与用户交互。
Model: 实体模型。
ViewModel: 负责完成View与Model间的交互,负责业务逻辑。
在常规的开发模式中,数据变化需要更新UI的时候,需要先获取UI控件的引用,然后再更新UI。获取用户的输入和操作也需要通过UI控件的引用。在MVVM中,这些都是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变也能自动反馈到数据层,数据成为主导因素。这样MVVM层在业务逻辑处理中只要关心数据,不需要直接和UI打交道,在业务处理过程中简单方便很多。
MVVM模式中,数据是独立于UI的。
数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。即便是控件改变了(比如:TextView换成EditText),ViewModel也几乎不需要更改任何代码。它非常完美的解耦了View层和ViewModel,解决了上面我们所说的MVP的痛点。
在MVVM中,数据发生变化后,我们在工作线程直接修改(在数据是线程安全的情况下)ViewModel的数据即可,不用再考虑要切到主线程更新UI了,这些事情相关框架都帮我们做了。
MVVM的分工是非常明显的,由于View和ViewModel之间是松散耦合的:一个是处理业务和数据、一个是专门的UI处理。所以,完全由两个人分工来做,一个做UI(XML和Activity)一个写ViewModel,效率更高。
一个ViewModel可以复用到多个View中。同样的一份数据,可以提供给不同的UI去做展示。对于版本迭代中频繁的UI改动,更新或新增一套View即可。如果想在UI上做A/B Testing,那MVVM是你不二选择。
有些同学一看到单元测试,可能脑袋都大。是啊,写成一团浆糊的代码怎么可能做单元测试?如果你们以代码太烂无法写单元测试而逃避,那可真是不好的消息了。这时候,你需要MVVM来拯救。
我们前面说过了,ViewModel层做的事是数据处理和业务逻辑,View层中关注的是UI,两者完全没有依赖。不管是UI的单元测试还是业务逻辑的单元测试,都是低耦合的。在MVVM中数据是直接绑定到UI控件上的(部分数据是可以直接反映出UI上的内容),那么我们就可以直接通过修改绑定的数据源来间接做一些Android UI上的测试。
在学习MVVM架构思想之前,首先需要明白dataBinding这个工具如何使用。
在懂了如何使用dataBinding的情况下,再来思考架构模式,我的思路是,这个架构既然是由MVP演化而来,那么我们完全可以按照设计Poresenter的方法来设计ViewModel,ok,这是第一步,然后第二步,我们presenter里边持有了View和Model的引用,并根据业务逻辑对二者进行组织,那么,我们的ViewModel也根据这种思路来写,既然是已数据为驱动,编写设计这个架构代码的切入点肯定还是从Model.loadData()进行切入。在拿到数据之后,我们不再需要像VC或VP那样要考虑”这个数据该放在哪个View控件上”这类问题。
M:数据Bean或IModel,和MVP一样
V:Activity和xml布局
VM:Presenter的进化,只是数据会利用dataBinding框架的双向绑定特性对数据进行绑定,最直观的结果就是我们设置数据不再是拿到某个具体的View,而是利用框架生成的Binding文件setXXX(Bean),譬如ActivityMainBinding.setUser(user)
AAC入门我之前写过一篇文章,要看的同学戳这里
到了这一步,侧重点在于与传统DataBinding框架的纵向对比,而非与MVP,MVC的横向对比。
还是简要说说AAC的特点:
它有个几个成员:LifeCycle,LiveData,Room,ViewModel。其中前三个都是为ViewModel服务的辅助。抛开Room不谈(数据存储),LiveData和LifeCycle都是和LifecycleRegistryOwner,LifecycleObserver相关的,它们包含声明周期与观察者模式俩层含义。使用观察者模式是为了让数据绑定成为可能,它提供了事件传递的属性。
一般来说,我们封装一个MVVM By AAC的架构遵循以下套路:
DataBean extends LiveData impl LifecycleObserver,让Model具备被传递的可能以及和生命周期绑定
是Activity,通过liveData的observe接收ViewModel传过来的事件传递
final MutableLiveData> users = model.getUsers();
MainActivity extends AppCompatActivity implements LifecycleRegistryOwner {
users.observe(this, new Observer>() {
@Override
public void onChanged(@Nullable List userLiveData) {
Log.e("MainActivity","更新UI");
}
});
}
负责加载数据,逻辑编写,以及将Model以观察者事件形式传递给View
public class UserViewModel extends ViewModel{
private MutableLiveData> users;
public MutableLiveData> getUsers() {
if(null == users){
users = new MutableLiveData();
}
loadUsers();
return users;
}
private void loadUsers() {
// setValue
}
}
以下例子是纯框架的使用,并没有进行MVVM的封装,只是为了展示,即使我不继承LiveData这些乱七八糟的类就能在API26+上实现这个功能,可以把这个例子当做练习,改造一下。
package proxy.zj.com.networklivedata.bean;
/**
* Created by thinkpad on 2018/2/27.
*/
public class TestData {
String content = "";
public TestData(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
public class OkGoActivity extends AppCompatActivity {
private TextView content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ok_go);
content = findViewById(R.id.content);
//"https://api.douban.com/v2/movie/top250?start=0&count=10"
OkGo.init(this.getApplication());
//***************************************获取数据********************************************
final OkGoViewModel model = ViewModelProviders.of(this).get(OkGoViewModel.class);
findViewById(R.id.getdata).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MutableLiveData data = model.getData();
data.observe(OkGoActivity.this, new Observer() {
@Override
public void onChanged(@Nullable TestData o) {
content.setText(o.getContent());
}
});
}
});
//*******************************************************************************************
}
}
M:数据Bean和IModel,数据请求一般写在IModel(impl)
V:xml布局文件
C:Activity,直接持有Model引用,在Activity内直接请求数据,然后再绘制UI
Moudel:
Model还是保持MVC的特性,数据Bean和IModel,数据请求一般写在IModel(impl)
View:
Activity,XML,相比MVC,不单单的再将xml布局文件视为View,Activity也不再作为一个Controller而是作为View的一部分而存在,这也是解耦的一个体现,也符合了Android设计Activity这个组件的初衷。
Presenter:
由Controller进化而来,持有View和Modle的引用,根据业务逻辑将二者进行梳理放置
另外关于MVP的代码实践我总结了一张脑图,大分辨率的,点击下载
mvvm对mvp的横向演进,用(单/双向)绑定机制解放了我们拿到数据关注某个具体控件,并隐式的绑定了View层的生命周期,在内存泄漏上有一定优势。
mvvm对自身的纵向演进,由谷歌2015原生的dataBinding框架到谷歌2017 AAC标准框架(LiveData LifeCycle ViewModel) 。
https://www.jianshu.com/p/a898ef83f38c
https://www.jianshu.com/p/53925ccb900e
https://www.jianshu.com/p/c0988e7f31fd //Android官方MVVM框架实现组件化之整体结构(强烈推荐)