View近段时间在实际应用项目中接触到了MVVM模式编写的代码,发现与MVP有很大的不同,这里做一下个人记录。其实关于MVVM模式的原理我们大家在很早之前就接触过,网上也有很多释义,我就不多做解释,大家需要的话可以打开链接看一下,我在这里挑选了几篇 Android中MVVM是什么 Android开发之MVVM新姿势探究 MVC MVP MVVM 谈谈我对Android应用架构的理解
一句话了解MVVM
- View 只处理用户的即时交互;
- ViewModel 只处理业务逻辑;
- Model 只处理数据存储与获取。
View
View层,不再是我们之前理解的是一个TextView、LinearLayout等View控件,只要是可以和用户进行交互的都可以归属到View层,比如:Activity、Fragment、Dialog、PopupWindow、XML布局、布局Adapter、系统及自定义View控件等等,只要是用户看到的、摸到的都归View层管。也就是说禁止在这里做业务逻辑、数据操作等和View无直接相关的事情。
Model
model层,与我们之前把定义的实体bean对象称为model不同的是,这里的model被赋予了数据管理的职责。数据的管理包括数据存储与数据获取,这里存储的位置不仅限于本地(SharedPreferences、SQLite),而且也会是网络上的任何存储方式。
ViewModel
ViewModel层,说是只处理业务逻辑,更准确的说法是Model层和View层的粘合剂,从Model中获取数据整合之后提供给View层进行显示,响应View层的事件调用Model层进行响应的落地。
他们的关系
通过以上大致了解了View-ViewModel-Model是什么、主要干什么,通过这三个层次的定义把视图、业务、数据进行了切割,这样职责与分层清楚了,那他们之间怎么传递数据呢?
View层包含两大类,Activity、Fragment、Dialog、PopupWindow等代码类和XML布局类,代码类与布局类交互的方式是DataBinding,通过DataBinding的双向绑定技术可以轻松实现View数据的双向通知。
Model层数据的获取与保存绝大部分应该是在服务端的,网络访问是数据交互的重头戏,这部分必须在异步中完成,异步处理方面大家都弃Handler而去投奔了Rx的怀抱,所有在Model层的网络部分与ViewModel层交互通过Rx的观察者模式Observable。
ViewModel层从Model层获取数据后,需求将数据通知到View层并显示出来,采用Android架构组件中的LiveData。
Demo
需求一
需求内容
编写字符串,保存到本地,再读取出来修改下并保存。
撸码
布局很简单,上面一个EditText,下面是两个Button。
也许有朋友对这种布局的编写有些疑惑,请看《Android中的双向绑定》,对Android中的databinding进行初识。其中android:text="@={view.content}"是做数据双向绑定用的;
android:onClick="@{view::onSaveClick}"即可调用view中的onSaveClick(view)方法。
<?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 class="com.kevin.mvvmdemo.MainActivityBinding">
<variable
name="view"
type="com.kevin.mvvmdemo.MainActivity" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">
<EditText
android:id="@+id/et_content"
android:layout_width="0dp"
android:layout_height="125dp"
android:background="#EEEEEE"
android:hint="请输入内容"
android:inputType="textMultiLine"
android:padding="8dp"
android:text="@={view.content}"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_save"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="保存"
android:onClick="@{view::onSaveClick}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_load"
app:layout_constraintTop_toBottomOf="@+id/et_content"
app:layout_constraintVertical_chainStyle="spread" />
<Button
android:id="@+id/btn_load"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="加载"
android:onClick="@{view::onLoadClick}"
app:layout_constraintLeft_toRightOf="@+id/btn_save"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_content"
app:layout_constraintVertical_chainStyle="spread" />
</android.support.constraint.ConstraintLayout>
</layout>
编写MainActivity
创建MainActivity,Activity中为设置databinding及两个回调方法,onSaveClick(view)
、onLoadClick(view)
这两个方法是在XML布局中调用的。
public class MainActivity extends AppCompatActivity {
private MainActivityBinding mBinding;
public MutableLiveData<String> content = new MutableLiveData<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = MainActivityBinding.inflate(getLayoutInflater());
mBinding.setLifecycleOwner(this);
setContentView(mBinding.getRoot());
mBinding.setView(this);
}
public void onSaveClick(View view) {
Toast.makeText(this, "点击了 保存 按钮", Toast.LENGTH_SHORT).show();
}
public void onLoadClick(View view) {
Toast.makeText(this, "点击了 加载 按钮", Toast.LENGTH_SHORT).show();
}
}
目前看到的效果是这样的,Activity中响应XML布局的的点击。
注意
这里需要注意的是,在《认识Android中的双向绑定》中可观察对象使用的是:
public ObservableField<String> content = new ObservableField<>();
这里使用的是:
public MutableLiveData<String> content = new MutableLiveData<>();
其中ObservableField是databinding中提供的,MutableLiveData是android arch 中提供的,二者都可以,Google推荐是MutableLiveData,Android架构会管理它的生命周期,在使用MutableLiveData,需要添加如下:
mBinding.setLifecycleOwner(this);
编写MainRepository
创建MainRepository,Repository
主要做的就是数据的获取与保存,这里简单的保存在SharedPreferences
,提供了保存和读取的两个方法。
public class MainRepository {
private Application mApplication;
public MainRepository(Application application) {
this.mApplication = application;
}
public void saveData(String content) {
SharedPreferences sp = mApplication.getSharedPreferences("demo", Context.MODE_PRIVATE);
sp.edit().putString("content", content).commit();
}
public String loadData() {
SharedPreferences sp = mApplication.getSharedPreferences("demo", Context.MODE_PRIVATE);
return sp.getString("content", "");
}
}
编写MainViewModel
创建MainViewModel,继承自Android架构组件中的ViewModel或者AndroidViewModel,其中AndroidViewModel运行传递Application对象。
作为业务逻辑的处理者以及Model层与View层的粘结剂,这里没有需要处理的物业逻辑,仅仅是对数据层封装了一下,提供给View层使用。
public class MainViewModel extends AndroidViewModel {
private MainRepository mRepository;
public MainViewModel(@NonNull Application application) {
super(application);
mRepository = new MainRepository(application);
}
public void setData(String content) {
mRepository.saveData(content);
}
public String getData() {
return mRepository.loadData();
}
}
修改MainActivity
添加依赖库
实例化ViewModel需要用到Android架构组件中的另外一个库,在build.gradle中添加依赖:
dependencies {
// ... ...
implementation 'android.arch.lifecycle:extensions:latest.release'
}
实例化ViewModel
在onCreate()方法中初始化ViewModel:
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
调用ViewModel方法
之后就可以在点击的监听中进行响应的操作啦,代码如下:
public void onSaveClick(View view) {
mViewModel.setData(content.getValue());
}
public void onLoadClick(View view) {
String data = mViewModel.getData();
content.setValue(data);
}
完整代码
public class MainActivity extends AppCompatActivity {
public MutableLiveData<String> content = new MutableLiveData<>();
private MainActivityBinding mBinding;
private MainViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = MainActivityBinding.inflate(getLayoutInflater());
mBinding.setLifecycleOwner(this);
setContentView(mBinding.getRoot());
mBinding.setView(this);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
}
public void onSaveClick(View view) {
mViewModel.setData(content.getValue());
}
public void onLoadClick(View view) {
String data = mViewModel.getData();
content.setValue(data);
}
}
效果
通过以上,就完成了一个最简单的MVVM,效果如下: