3.3 ViewModel的生命周期

在前面的例子中,提到了不能将View传给ViewModel,否则会引起内存泄漏,这是因为ViewModel的生命周期要比Activity的长很多,如果ViewModel引用View则会导致当视图被销毁的时候引用的资源得不到释放,从而导致内存泄漏。在Activity屏幕旋转前后,ViewModel的生命周期状态如图3-5所示。

039-1

图3-5 Activity旋转对应ViewModel的生命周期

从图3-5中可以看出,ViewModel在Activity旋转屏幕被销毁又重新创建的整个过程中一直存在,这也就是不能在ViewModel中引用视图、Activity上下文等变量的原因。

到这里,可能会有读者有疑问,在onCreate中创建了ViewModel对象,屏幕旋转后又重新创建了对象,为什么说ViewModel对象是一直存在的呢?这得从创建ViewModel对象的方法来看,具体如下:

advertisingViewModel = ViewModelProvider(this).get(AdvertisingViewModel::class.java)

这里要注意的是,Kotlin为开发者提供了许多扩展插件。在实际项目开发中,可以借助KTX更简单地创建ViewModel实例。以在Activity中创建ViewModel为例,在build.gradle中添加KTX扩展,代码如下:

dependencies {
    implementation "androidx.fragment:fragment-ktx:1.3.4"
}

添加完之后可以通过下列代码获取ViewModel:

val advertisingViewModel by viewModels<AdvertisingViewModel>()

这种实现方式简洁许多,也是开发者在项目中常用的方式。这里读者先做了解即可,下面继续看ViewModelProvider(this).get方法。ViewModelProvider(this).get方法的主要源码如下:

public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

在modelClass不为null的情况下,程序会走到get方法中,get方法的主要代码如下:

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        return (T) viewModel;
    } else {
        if (viewModel != null) {
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

从get方法中可以明显地看出,创建ViewModel的方法采用的是工厂模式,创建好之后将其缓存在ViewModelStore中。如果当前需要创建的ViewModel对象已经存在,则直接从ViewModelStore中取出。所以在屏幕旋转前后使用的ViewModel是同一个对象。也就是说在创建ViewModel的时候,只要传入的class对象是一样的,那么获取到的ViewModel就是同一个对象,基于这一点可以很轻松地在同一个宿主Activity的不同Fragment之间进行数据共享。