ViewModel的简单实例

概述

ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI 相关数据,
ViewModel中数据会一直存活即使 activity configuration发生变化,比如横竖屏切换的时候。

数据持久化

ViewModel出现之前我们可以用activityonSaveInstanceState()机制保存和恢复数据,但缺点很明显,它只适合保存少量的可以被序列化、反序列化的数据,假如我们需要保存是一个比较大的比如List<Bitmap>这种机制明显就不合适。

解耦

从最早的MVC到目前流行的MVP、MVVM目的无非是明确职责,分离UI controller负担
UI controller 比如Activity 、Fragment是设计用来渲染展示数据、响应用户行为、处理系统的某些交互等。如果再要求他去负责加载网络或数据库数据,会让其显得臃肿和难以管理。
所以为了简洁、清爽、丝滑,我们可以分离出数据操作的职责给 ViewModel

异步回调

通常我们 app 需要频繁异步请求数据,比如调接口请求服务器数据。
当然这些请求的回调都是相当耗时的,之前我们在 Activity 或 fragment里接收这些回调。所以不得不考虑潜在的内存泄漏情况,比如 Activity 被销毁后接口请求才返回。
处理这些问题,会给我们增添好多复杂的工作。但利用 ViewModel 处理数据回调,可以完美的解决此痛点。

注意事项

ViewModel绝对不要持有View,Lifecycle以及其他任何可能持有Activity Context的类的引用

ViewModel绝对不能去观察一个生命周期感知的被观察者,例如LiveData

注意在进行Adapter更新时,列表项要单独在Activity中声明,不可直接使用传入的参数,因为对象不同会导致更新不到数据

ViewModelProviders

MyViewModel mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

ViewModel的实例化并不是通过普通的new来完成的,而是通过ViewModelProviders来完成。它会去判断ViewModel是否存在,若存在则直接返回,否则它会去创建一个ViewModel
之所以这么写,是因为viewModel的生命周期长于Activity.如果我们在onCreate()方法中创建ViewModel的实例,那么每次onCreate方法执行的时候,ViewModel都会创建一个新的实例,当手机屏幕发生旋转的时候,就无法保留其中的数据。

现以弃用此方式,改为

MyViewModel mViewModel = new ViewModelProvider(this).get(MyViewModel.class);

Factory

在大多数情况下,我们需要通过构造函数来传递一些参数,我们可以借助ViwModelProvider.Factory来实现

onCleared

ViewModel是一个抽象类,其中只有一个方法onCleared(),当ViewModel不再被需要的时候,也就是与之相关的Activity都被销毁时,该方法会被系统调用,我们可以在这个方法里面执行一些资源释放的操作,以免内存泄漏。
由于屏幕旋转导致的Activity重建,但该方法不会被调用,只有ViewModel已经没有任何Activity与之有关联,系统才会调用此方法。

public class MyViewModel extends ViewModel
{
    /**
     * 与之相关的Activity被销毁时,该方法会被系统调用。
     * 我们可以在该方法中执行一些资源释放的相关操作
     */
    @Override
    protected void onCleared()
    {
        super.onCleared();
    }
}

注意:既然ViewModel的销毁是由系统来判断和执行的,那么系统是如何判断的呢?是根据Context引用。因此,我们在使用ViewModel的时候,千万不能从外面传入Activity,Fragment或者View之类的含有Context引用的东西,否则系统会认为该ViewModel还在使用中,从而无法被系统销毁回收,导致内存泄漏的发生。

但如果你希望在ViewModel中使用Context怎么办呢?我们可以使用AndroidViewModel类,它继承自ViewModel,并且接收Application作为Context,既然是Application作为Context,也就意味着,我们能够明确它的生命周期和Application是一样的,这就不算是一个内存泄露了。

AndroidViewModel
如果ViewModel需要Applicaiton的Context(为了获取系统服务)则可借助AndroidViewModel

  1. 自定义一个类,继承自AndroidViewModel
  2. 构造器一定要有一个唯一参数-Application'的对象

ViewModel 生命周期图

请添加图片描述

数据共享

比如在一个Activity里有多个fragment之间需要做某些交互。之前的做法是接口回调,需要统一在Activity里管理,并且不可避免的fragment之间还得互相持有对方的引用。耦合度高不说,还需要大量的容错判断(比如对方的fragment是否还活着)。

activity 与其内部的fragment可以共用同一个 ViewModel

  • Activity 不需要做任何事,甚至不知道这次交互,完美解耦。
  • Fragment 只需要与ViewModel交互,不需要知道对方Fragment的状态甚至是否存在,更不需要持有其引用。
  • Fragment 生命周期互不影响,甚至fragment替换成其他的也不影响这个系统的运作。

ViewModel依托于Activity进行实例化,所以数据共享只能在同一个Activity内进行,跨Activity是不能进行数据共享的,会创建新的实例而导致获取不到前一个的数据。
较好的数据共享实例是Activity与下面的Fragment之间的数据共用,要注意ViewModelProvider的提供即传入的owner要是同一个Activity

SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected;
    public LiveData<Item> getSelected() {
        if(selected == null)
            selected = new MutableLiveData<>();
        return selected;
    }
    /* 初始化数据或外部绑定数据 */
    // 也可以在外部直接使用 sharedViewModel.getSelected().setValue()
    public void select(Item item) {
        selected.setValue(item);
    }
}
/* 可以在其中一个Fragment或者Activity中进行数据的绑定*/
public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}
/* 在其它Fragment或者Activity中进行数据变更的监听*/
public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

用法实例

添加依赖

//lifecycle
def lifecycle_version = "2.3.1"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" // ViewModel
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" // LiveData

加载用户信息列表

创建MyViewModel必须继承自ViewModel

  1. 将数据相关的逻辑都放置到ViewModel中,如:存放一个用户信息列表
  2. ViewModel还需要处理加载用户信息列表的逻辑
public class MyViewModel extends ViewModel {
  // 1. 用户信息,列表
    private MutableLiveData<List<User>> users;
  // 2. 获取到用户信息列表
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }
  // 3. 实际加载用户信息列表,可以从数据库或者网络中
    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

注册监听

一定要在onCreate()中进行处理,当Activity销毁时framework会调用ViewModel的onCleared()方法对资源进行清理

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // 1. 第一次执行会创建ViewModel,Activity重建后调用到该方法会获取到之前的ViewModel
        MyViewModel mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        // 2. 注册对LiveData的观察,将本Activity作为观察者
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值