前言
MVVM的架构模式,在前端使用React同学,或者在用Flutter开发的同学,都能体会到React的思想用数据直接渲染UI的方便。在Android引入jetpack组件后,如果你还在使用MVP或者MVC的架构方式,赶紧来使用下吧,绝对会让你说出两个字 “真香”。
这篇文章主要是介绍如何在项目中使用databinding + ViewModel +LiveData,体会下Android Jetpack给我们带来的方便。
实现了单个业务使用ViewModel以及多个Fragment中通信使用viewModel
知识点大纲
使用MVVM
环境配置
请在应用模块的 build.gradle 文件中添加 dataBinding 元素
android {
...
dataBinding {
enabled = true
}
}
依赖最新的jetpack家族,可以按需依赖, 要是你的项目里的ViewModel和LiveData还不是最新的话 建议升级到最新版本
dependencies {
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// Annotation processor
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// optional - helpers for implementing LifecycleOwner in a Service
implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
// optional - ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"
// optional - Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"
}
实战部分
在这里我们模仿实现一下异步请求数据刷新我们的UI界面和同步修改页面。
databinding 数据视图绑定,布局修改
这里我们需要定义两个变量,一个是我们试图所对应的对象以及设置相关的点击事件
activity_view_model.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="click"
type="android.view.View.OnClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_async_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="异步修改数据,3秒后显示"/>
<TextView
android:id="@+id/tv_name"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击按钮,同步修改数据"/>
<Button
android:id="@+id/btn_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击修改数据"
android:onClick="@{click::onClick}"/>
</LinearLayout>
</layout>
UserBean 对象
public class UserBean {
private String firstName;
private String lastName;
public UserBean(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
对应的ViewModel
这里我们用一个耗时操作来模仿网络请求, 定时操作所在的是工作线程,这里需要使用 postValue
。
public class UserViewModel extends ViewModel {
private MutableLiveData<UserBean> users;
private Disposable mTimeLimitDisposable;
protected BaseSchedulerProvider mSchedulerProvider;
private MutableLiveData<String> currentName; //定义String变量
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
public UserViewModel() {
mSchedulerProvider = new BaseSchedulerProvider() {
@NonNull
@Override
public Scheduler computation() {
return Schedulers.trampoline();
}
@NonNull
@Override
public Scheduler io() {
return Schedulers.trampoline();
}
@NonNull
@Override
public Scheduler ui() {
return Schedulers.trampoline();
}
};
}
public LiveData<UserBean> getUsers() {
if (users == null) {
users = new MutableLiveData<UserBean>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
//通过延时操作模仿网络请求
if (mTimeLimitDisposable != null) {
mTimeLimitDisposable.dispose();
}
mTimeLimitDisposable = Flowable.intervalRange(0, 3000, 0, 1, TimeUnit.MILLISECONDS)
.onBackpressureDrop()
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.computation())
.doOnError(throwable -> {
})
.doOnComplete(this::getUserDataDone)
.subscribe();
}
private void getUserDataDone() {
Log.e("aaa", "getUserDataDone");
Log.e("aaa","当前线程ID:" +Thread.currentThread().getId());
UserBean userBean = new UserBean("第一个名字", "第二个名字");
users.postValue(userBean);
}
@Override
protected void onCleared() {
super.onCleared();
if (mTimeLimitDisposable != null) {
mTimeLimitDisposable.dispose();
}
}
}
ViewModelFactory
public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static volatile ViewModelFactory INSTANCE;
public static ViewModelFactory getInstance(){
if(INSTANCE == null){
synchronized (ViewModelFactory.class){
if(INSTANCE == null){
INSTANCE = new ViewModelFactory();
}
}
}
return INSTANCE;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return super.create(modelClass);
}
}
ViewModelActivity
这里按钮点击实现了一个同步修改页面的操作,直接使用 setValue
。这边页面通过监听数据的变化来做修改
public class ViewModelActivity extends AppCompatActivity implements View.OnClickListener {
private ActivityViewModelBinding mViewModelBinding;
private UserViewModel mModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
mViewModelBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_model);
mViewModelBinding.setClick(this);
Log.e("aaa","当前主线程111ID:" +Thread.currentThread().getId());
mModel = new ViewModelProvider(this.getViewModelStore(),
ViewModelFactory.getInstance()).get(UserViewModel.class);
mModel.getUsers().observe(this, users -> {
// update UI
Log.e("aaa", "收到了");
Log.e("aaa","当前主线程222ID:" +Thread.currentThread().getId());
mViewModelBinding.tvAsyncName.setText(users.getFirstName());
});
mModel.getCurrentName().observe(this, new Observer<String>() {
@Override
public void onChanged(String curName) {
Log.e("aaa", "修改model的数据");
mViewModelBinding.tvName.setText(curName);
}
});
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_click) {
double index = (int) (Math.random() * 100);
String anotherName = "随机展示:" + index;
mModel.getCurrentName().setValue(anotherName);
}
}
}
以上就是用MVVM来完成整个业务的开发的具体代码。
利用ViewModel来实现两个子Fragment通信
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。想象一下主从 Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
如果使用ViewModel,将会有如下优势
-
Activity 不需要执行任何操作,也不需要对此通信有任何了解。
-
除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
-
每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
MasterFragment 主fragment
public class MasterFragment extends Fragment {
private SharedViewModel sharedViewModel;
private Button mBtn;
//....
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,
// 它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
mBtn.setOnClickListener(v -> {
Log.e("aaa", "点击---->");
double index = (int) (Math.random() * 100);
String anotherName = "随机展示:" + index;
sharedViewModel.select(anotherName);
});
}
}
DetailFragment 子fragment
public class DetailFragment extends Fragment {
private TextView mTvText;
//....
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// SharedViewModel model = new ViewModelProvider(this.getViewModelStore(),
// ViewModelFactory.getInstance()).get(SharedViewModel.class);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), new Observer<String>() {
@Override
public void onChanged(String s) {
Log.e("aaa", "change----->" + s);
mTvText.setText(s);
}
});
}
}
SharedViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData<String> selected = new MutableLiveData<String>();
public void select(String item) {
selected.setValue(item);
}
public LiveData<String> getSelected() {
return selected;
}
}
ViewModel
ViewModel 的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
示例代码链接
小结
通过上述例子,ViewModel的使用能帮我们提高我们的日常业务开发,在使用ViewPager中嵌套多个Fragment中,ViewModel能帮我们更好的解耦子fragment之间的通信,当我用了ViewModel之后,才发现使用此组件真香。