1.MVVM
MVVM是Model-View-ViewModel的简写,它是MVC的改进版。其中ViewModel将视图UI和业务逻辑分开,它可以取出Model的数据同时帮忙处理View中由于需要展示内容而涉及的业务逻辑。
MVVM采用DataBinding双向数据绑定,View中数据变化将自动反映到ViewModel上;反之,Model中数据变化也会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。
MVVM核心思想是关注Model的变化,让MVVM框架利用自己的机制自动更新DOM,也就是所谓的数据-视图分离,数据不会影响视图。
MVVM的本质是数据驱动,把解耦做的更彻底,viewModel不持有view 。
View层:界面层,对应于Activity和XML,负责数据显示及用户交互。相比MVP的view,这里面的 view视图数据一般是在xml中使用DataBinding进来双向绑定数据的。View层用于监听UI事件和生命周期,通知到ViewModel;由ViewModel通知数据更新、刷新UI展示。
ViewModel层:关联层,作为中间桥梁去通知model数据层处理数据业务,并将结果回调给UI层处理UI逻辑。ViewModel层只负责业务处理,这一点跟MVP的P层功能相同;它不持有View层的引用,这一点跟MVP的P层不相同,MVP的P层会通过View层暴露的接口间接地“持有”对UI控件的引用,而ViewModel层则是完全不会引用到View层的UI控件。
Model层:数据层,包含数据实体和对数据实体的操作,和MVP的Model没有区别。所有的数据处理都在这一层完成,然后统一暴露给ViewModel使用,例如触发LiveData。
MVVM的调用关系和MVP一样。但是,在ViewModel中会有一个叫Binder,或者是Data-binding engine的东西。以前全部由Presenter负责的View和Model之间数据同步操作交由给Binder处理。开发者只需要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对Model进行更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操作(例如表单输入)时,Binder也会自动把数据更新到Model上去。这种方式就是双向数据绑定。
2.MVVM和MVP
MVP和MVVM的区别:ViewModel与View绑定后,ViewModel与View其中一方的数据更新都能立即通知到对方,而Presenter需要通过接口去通知View进行更新。
也就是说,MVVM把View和Model的同步逻辑自动化了。MVP中Presenter负责的View和Model同步不再手动地进行操作,而是交由框架所提供的Binder进行负责。只需要告诉Binder,View显示的数据对应的是Model哪一部分即可。
MVVM的优点:
低耦合。相比于MVP中Presente与View存在耦合,ViewModel与View使用双向绑定机制,耦合更低。
ViewModel里只包含数据和业务逻辑,没有UI的东西,方便单元测试。ViewModel只负责处理和提供数据,UI的改变(比如TextView替换EditText)ViewModel几乎不需要更改任何代码,只专注于数据处理就可以了。
简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确,大大减少了对View同步更新的测试。
MVVM的缺点:
数据绑定使得程序较难调试,界面出现异常时,有可能是View的代码有问题,也可能是Model的代码有问题。由于数据绑定使得数据能够快速传递到其他为止,因此要debug定位出异常就比较有难度了。
过于简单的图形界面不适用,或说牛刀杀鸡。
对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
3.MVVM实例
①model层
实体类:
public class Account extends BaseObservable {
private String name;
private int level;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Bindable
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
notifyPropertyChanged(BR.level);
}
}
模拟从网络获取账号数据:
public class MyModel {
public void getAccountData(String accountName, MCallback callback) {
Random random = new Random();
boolean isSuccess = random.nextBoolean();
if (isSuccess) {
Account account = new Account();
account.setName(accountName);
account.setLevel(100);
callback.onSuccess(account);
} else {
callback.onFailed();
}
}
}
回调接口:
public interface MCallBack {
void onSuccess(Account account);
void onFail();
}
②View层
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.test.demo.MyViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- @= :数据和视图双向绑定 -->
<EditText
android:id="@+id/ed_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="@={viewModel.userInput}"
android:hint="请输入要查询的账号" />
<Button
android:id="@+id/btn_get_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="80dp"
android:onClick="@{viewModel.getData}" //在view层通过ViewModel层的getData方法获取到要显示的信息(ViewModel层的getData方法实际上调用的是Model层的方法)
android:text="获取账号信息" />
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="80dp"
android:text="@{viewModel.result}" //在view层获取ViewModel层的result,显示到界面上
android:hint="账号信息暂未获取" />
</LinearLayout>
</layout>
Activity文件:
public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMvvmBinding mvvmBinding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
MyViewModel myViewModel = new MyViewModel(getApplication(), mvvmBinding);
mvvmBinding.setViewModel(myViewModel);
}
}
③ViewModel层
public class MyViewModel extends BaseObservable {
private MyModel mvvmModel;
private ActivityMvvmBinding mvvmBinding;
private String result;
private String userInput;
//一般需要传入Application对象,方便在ViewModel中使用Application,比如sharedPreferences需要使用
public MyViewModel(Application application) {
mvvmModel = new MyModel();
}
public MyViewModel(Application application, ActivityMvvmBinding binding) {
mvvmModel = new MyModel();
this.mvvmBinding = binding;
}
public void getData(View view) {
//使用了DataBinding双向绑定,不需要手动获取输入框的数据,故注释掉
// String userInput = mvvmBindingl.edAccount.getText().toString();
myModel.getAccountData(userInput, new MCallback() { //获取信息的实际操作在Model层
@Override
public void onSuccess(Account account) {
String info = account.getName() + "|" + account.getLevel();
setResult(info); //通过接口回调拿到获取的结果
}
@Override
public void onFailed() {
setResult("获取数据失败");//通过接口回调拿到获取的结果
}
});
}
@Bindable
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
notifyPropertyChanged(BR.result);
}
@Bindable
public String getUserInput() {
return userInput;
}
public void setUserInput(String userInput) {
this.userInput = userInput;
notifyPropertyChanged(BR.userInput);
}
}
文章评论