MVVM模式与MVP模式一样,也将应用分为三层,并且各个对应的层的职责相似:
Model层,主要负责数据的提供。Model层提供业务逻辑的数据结构(比如,实体类),提供数据的获取(比如,从本地数据库或者远程网络获取数据),提供数据的存储。
View层,主要负责界面的显示。View层不涉及任何的业务逻辑处理,它持有ViewModel层的引用,当需要进行业务逻辑处理时通知ViewModel层。
ViewModel层,主要负责业务逻辑的处理。ViewModel层不涉及任何的视图操作。通过官方提供的Data Binding库,View层和ViewModel层中的数据可以实现绑定,ViewModel层中数据的变化可以自动通知View层进行更新,因此ViewModel层不需要持有View层的引用。ViewModel层可以看作是View层的数据模型和Presenter层的结合。
MVVM模式与MVP模式最大的区别在于:ViewModel层不持有View层的引用。这样进一步降低了耦合,View层代码的改变不会影响到ViewModel层。
MVVM模式相对于MVP模式主要有如下优点:
进一步降低了耦合。ViewModel层不持有View层的引用,当View层发生改变时,只要View层绑定的数据不变,那么ViewModel层就不需要改变。而在MVP模式下,当View层发生改变时,操作视图的接口就要进行相应的改变,那么Presenter层就需要修改了。
不用再编写很多样板代码。通过官方的Data Binding库,UI和数据之间可以实现绑定,不用再编写大量的findViewById()和操作视图的代码了。总之,Activity/Fragment的代码可以做到相当简洁。
例子
下面举一个简单的例子来实践MVVM模式。
例子实现的主要功能是:输入内容,从网络获取返回结果并展示。主界面如下图所示:
访问该URL将返回一串JSON字符串,如下所示:
{"reason":"成功的返回","result":{"code":100000,"text":"你好呀,亲爱的。"},"error_code":0}
首先,编写实体类:
public class Data {
/**
* reason : 成功的返回
* result : {"code":100000,"text":"你好啊,希望你今天过的快乐"}
* error_code : 0
*/
private String reason;
private ResultBean result;
private int error_code;
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public ResultBean getResult() {
return result;
}
public void setResult(ResultBean result) {
this.result = result;
}
public int getError_code() {
return error_code;
}
public void setError_code(int error_code) {
this.error_code = error_code;
}
public static class ResultBean {
/**
* code : 100000
* text : 你好啊,希望你今天过的快乐
*/
private int code;
private String text;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}
编写ViewModel
ViewModel不涉及任何的视图操作,只进行业务逻辑的处理。通过官方提供的Data Binding库,当ViewModel中的数据发生变化时,UI将自动更新
public class MainViewModel {
public final ObservableField<String> input = new ObservableField<>();
public final ObservableField<String> text = new ObservableField<>();
private Call<Data> mCall;
public MainViewModel() {
}
public void queryWeather() {
mCall = RetrofitManager.get()
.create(IUserBiz.class)
.getUser(input.get(),"096e85b6bde6fc56b74d8b64066af33c");
Log.i("mCall", input.get());
mCall.enqueue(new Callback<Data>() {
@Override
public void onResponse(Call<Data> call, Response<Data> response) {
Data.ResultBean weatherInfo = response.body().getResult();
text.set(weatherInfo.getText());
}
@Override
public void onFailure(Call<Data> call, Throwable t) {
Log.i("onFailure", "onFailure: "+t.getMessage());
}
});
}
public void cancelRequest() {
if (mCall != null) {
mCall.cancel();
}
}
}
通过DataBinding ObserVable 双向绑定,若相应的对象、字段、集合中数据变化时候,那么UI将会自动更新数据
编写View
View不涉及任何的业务逻辑处理,只进行界面的显示。在xml布局文件中,通过官方提供的Data Binding库,将UI与ViewModel中的数据进行绑定,当ViewModel中的数据发生变化时,UI将自动更新。xml布局文件的代码如下所示:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="com.example.mymvvm.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="20dp"
android:gravity="center_vertical"
android:layout_marginBottom="200dp"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/cv"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.text}"
/>
</LinearLayout>
<LinearLayout
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginBottom="50dp"
android:layout_width="match_parent"
android:layout_height="50dp">
<EditText
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="50dp"
android:text="@={viewModel.input}"/>
<Button
android:layout_width="100dp"
android:layout_height="50dp"
android:text="发送"
android:onClick="@{() -> viewModel.queryWeather()}"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
在Activity中,通过官方提供的Data Binding库加载布局文件,创建ViewModel,并绑定View和ViewModel。
MainActivity的代码如下所示:
public class MainActivity extends AppCompatActivity {
private MainViewModel mViewModel;
// DataBinding
private ActivityMainBinding mDataBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 创建ViewModel
mViewModel = new MainViewModel();
// 绑定View和ViewModel
mDataBinding.setViewModel(mViewModel);
}
@Override
protected void onDestroy() {
mViewModel.cancelRequest();
super.onDestroy();
}
}
总结
MVVM模式将应用分为三层:Model层主要负责数据的提供,View层主要负责界面的显示,ViewModel层主要负责业务逻辑的处理。各个层职责单一,结构清晰,应用可以很方便地进行测试、维护和扩展。