Android架构组件之ViewModel

ViewModel概念及用途

ViewModel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。

引入ViewModel之前,存在如下几个问题:

  1. 通常Android系统来管理UI controllers(如Activity、Fragment)的生命周期,由系统响应用户交互或者重建组件,用户无法操控。当组件被销毁并重建后,原来组件相关的数据也会丢失,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
  2. UI controllers经常会发送很多异步请求,有可能会出现UI组件已销毁,而请求还未返回的情况,因此UI controllers需要做额外的工作以防止内存泄露。
  3. 当Activity因为配置变化而销毁重建时,一般数据会重新请求,其实这是一种浪费,最好就是能够保留上次的数据。
  4. UI controllers其实只需要负责展示UI数据、响应用户交互和系统交互即可。但往往开发者会在Activity或Fragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。

上述几个痛点正是ViewModel出现的原因。

ViewModel使用方式

Android架构组件提供了一个ViewModel帮助类来为UI controllers负责数据相关的工作,当配置变化组件销毁重建时,这些数据仍然可以保留。当新的组件重建后,可以立即使用之前保留的数据。下面的示例代码维护了一个User列表数据:

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asyncronous operation to fetch users.
    }
}

在Activity中可以按如下方式使用ViewModel:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle 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.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果Activity销毁重建,可以立即得到一个相同的MyViewModel实例,它是由之前销毁的Activity创建的。当宿主Activity最终销毁后,系统会调用ViewModel的onCleared()方法来释放资源。

由上面的例子可以知道,ViewModel的生命周期比特定viewLifecycleOwner(如Activity实现了LifecycleOwner接口)要长,因此ViewModel不要引用viewLifecycle或其他引用到Activity上下文的对象。

ViewModel中可以包含LifecycleObserver,如LiveData对象。如果ViewModel需要使用Application的上下文对象,则可以通过继承AndroidViewModel,并提供一个以Application为参数的构造函数。

ViewModel的生命周期

ViewModel的生命周期依赖于对应的Activity或Fragment的生命周期。通常会在Activity第一次onCreate()时创建ViewModel,ViewModel的生命周期一直持续到Activity最终销毁或Frament最终detached,期间由于屏幕旋转等配置变化引起的Activity销毁重建并不会导致ViewModel重建。借用官方示意图来解释一下:

这里写图片描述

上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。

一般通过如下代码初始化ViewModel:

viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);

this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。

Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。

Fragment之间分享数据

日常开发中,一个Activity中可能会有多个Fragment,且他们需要进行交互。例如一个Fragment展示列表,另一个Fragment展示选中列表对应的详情信息,之前我们可能会利用宿主Activity并定义几个接口来实现Fragment之间的交互,另外还得考虑Fragment是否已经创建或显示的问题。

上述痛点,可以使用ViewModel来解决,在Fragment之间可以共享ViewModel。示例代码如下:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

注意到上面两个Fragment都用到了如下代码来获取ViewModel:

SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。

Fragment间共享ViewModel的优点有:

  1. 宿主Activity不需要做任何事情,也不需要关心Fragment间交互的内容。
  2. Fragment只需要了解ViewModel的实现,而无需了解通信目标Fragment。即使一个Fragment已经销毁了,另一个Fragment也能正常工作。
  3. 每一个Fragment有自己的生命周期,并不受其他Fragment影响。
  4. Fragment之间解耦。

总结

  1. ViewModel职责是为Activity或Fragment管理、请求数据,当然具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件
  2. ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
  3. ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
  4. ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。

参考:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

我的微信公众号「不混青年」,id「buhunqingnian」,技术之外的分享:
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值