当前位置: 代码迷 >> 综合 >> 一篇文章了解Android MVVM开发 ----- ViewModel
  详细解决方案

一篇文章了解Android MVVM开发 ----- ViewModel

热度:91   发布时间:2023-12-17 02:08:42.0

前言

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"// ViewModelimplementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"// LiveDataimplementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"// Lifecycles only (without ViewModel or LiveData)implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"// Saved state module for ViewModelimplementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"// Annotation processorannotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"// alternately - if using Java8, use the following instead of lifecycle-compilerimplementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"// optional - helpers for implementing LifecycleOwner in a Serviceimplementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"// optional - ProcessLifecycleOwner provides a lifecycle for the whole application processimplementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"// optional - ReactiveStreams support for LiveDataimplementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"// optional - Test helpers for LiveDatatestImplementation "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><variablename="click"type="android.view.View.OnClickListener" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_async_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:text="异步修改数据,3秒后显示"/><TextViewandroid:id="@+id/tv_name"android:textSize="20sp"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="点击按钮,同步修改数据"/><Buttonandroid: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@Overridepublic Scheduler computation() {
    return Schedulers.trampoline();}@NonNull@Overridepublic Scheduler io() {
    return Schedulers.trampoline();}@NonNull@Overridepublic 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);}@Overrideprotected 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@Overridepublic <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;@Overrideprotected 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 UILog.e("aaa", "收到了");Log.e("aaa","当前主线程222ID:" +Thread.currentThread().getId());mViewModelBinding.tvAsyncName.setText(users.getFirstName());});mModel.getCurrentName().observe(this, new Observer<String>() {
    @Overridepublic void onChanged(String curName) {
    Log.e("aaa", "修改model的数据");mViewModelBinding.tvName.setText(curName);}});}@Overridepublic 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;//....@Overridepublic 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;//....@Overridepublic 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>() {
    @Overridepublic 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之后,才发现使用此组件真香。

  相关解决方案