最近要新启动一个项目,准备使用MVVM架构搭建;在之前的文章中,提到过MVVM架构,在当前主流的框架中,渐渐的数据驱动UI、声明式UI(例如JetPack新出的Compose组件)会成为主流,在这里先谈一下MVVM架构的思路。
1 LiveData ViewModel DataBinding
以上3个组件是JectPack中的热门组件,也是MVVM的核心;LiveData具备感知数据变化的能力,可以动态更新UI数据;ViewModel有从始而终的生命周期,可感知App的生命周期;DataBinding实现数据与UI的绑定,真正实现了数据驱动UI。
在MVVM架构中,M代表数据层,主要负责数据的获取;V代表View层,主要就是XML布局以及Activity、Fragment;VM层就是ViewModel,用于存储UI的数据,承接View层和Model层的引用。
ViewModel层
public class MyViewModel extends ViewModel {
//待监听的数据
private MutableLiveData<User> allUser;
//持有Model层的引用
private Model model;
public MyViewModel(){
allUser = new MutableLiveData<>();
model = new Model(this);
}
public void update(){
if(allUser != null){
//做耗时操作请求网络数据
model.loadData();
// allUser.setValue(user);
}
}
public MutableLiveData<User> getUser(){
return allUser;
}
}
Model层
public class Model {
//持有VM层的引用
private MyViewModel viewModel;
public Model(MyViewModel myViewModel){
this.viewModel = myViewModel;
}
public void loadData(){
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//等待2s获取数据
User user = new User("你好","12345");
//Cannot invoke setValue on a background thread
// allUser.setValue(user);
//post的区别
viewModel.getUser().postValue(user);
}
}).start();
}
}
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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.example.mvvmdemo.model.User" />
<variable
name="viewModel"
type="com.example.mvvmdemo.viewmodel.MyViewModel"/>
<variable
name="onClick"
type="android.view.View.OnClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.user.username}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_pwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_name"
android:text="@{viewModel.user.password}" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_pwd"
android:onClick="onClick"
android:text="更新数据"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
View层要用到的数据,在layout标签下用variable展示
//获取ViewModel层的实例
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
//View层和ViewModel层绑定
ActivityMainBinding viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//设置ViewModel
viewDataBinding.setViewModel(viewModel);
viewDataBinding.setLifecycleOwner(this);
//单击事件监听
viewDataBinding.setOnClick(this::onClick);
在Activity中,需要通过ActivityMainBinding 将所需要的的数据绑定在一起,例如ViewModel、单击监听等。
2 MVVM架构设计
BaseModel:主要做一些公共的操作,或者初始化操作
public class BaseModel<VM extends BaseViewModel>{
protected VM mViewModel;
public BaseModel(VM mViewModel){
this.mViewModel = mViewModel;
}
}
BaseViewModel:持有Model层的引用
public abstract class BaseViewModel<M extends BaseModel,T extends LiveData> extends ViewModel {
//在ViewModel层,需要持有Model层的引用
protected M model;
protected T data;
public BaseViewModel(){
model = getModelInstance();
data = createLiveData();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
protected abstract T createLiveData();
protected abstract M getModelInstance();
}
BaseActivity
public abstract class BaseActivity<VM extends BaseViewModel,VDB extends ViewDataBinding>
extends AppCompatActivity implements View.OnClickListener {
protected VM mViewModel;
protected VDB mViewDataBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(getLayoutId());
this.mViewModel = getViewModelInstance();
// new ViewModelProvider(this).get(mViewModel.getClass());
this.mViewDataBinding = DataBindingUtil.setContentView(this,getLayoutId());
//具体的绑定工作
initBinding();
initData();
}
protected abstract void initData();
protected abstract void initBinding();
protected abstract VM getViewModelInstance();
protected abstract int getLayoutId();
}
各类的具体实现
public class MainActivity extends BaseActivity<UserViewModel,ActivityMainBinding> {
@Override
protected void initData() {
}
@Override
protected void initBinding() {
mViewDataBinding.setViewModel(mViewModel);
mViewDataBinding.setLifecycleOwner(this);
mViewDataBinding.setOnClick(this::onClick);
}
@Override
protected UserViewModel getViewModelInstance() {
return new UserViewModel();
}
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void onClick(View v) {
//ViewModel执行获取数据的请求
mViewModel.update();
}
}
public class UserViewModel extends BaseViewModel<UserModel, MutableLiveData<User>> implements IContract.VM {
@Override
protected MutableLiveData<User> createLiveData() {
return new MutableLiveData<>();
}
@Override
protected UserModel getModelInstance() {
//实例化具体的Model类
return new UserModel(this);
}
@Override
public void update() {
//通知Model层更新数据
if(model != null){
model.update();
}
}
}
public class UserModel extends BaseModel<UserViewModel> implements IContract.M {
public UserModel(UserViewModel mViewModel) {
super(mViewModel);
}
@Override
public void update() {
//耗时操作更新数据
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//等待2s获取数据
User user = new User("你好","12345");
//Cannot invoke setValue on a background thread
// allUser.setValue(user);
//post的区别
mViewModel.getData().postValue(user);
}
}).start();
}
}