Jetpack - 认识ViewModel

718 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

简介

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

ViewModel是一个抽象类,内部值得关注就是onCleared()方法,我们可以在此方法中释放资源等操作。

ViewModelStore是个很重要的成员,它主要就是用来保存ViewModel对象,保证Activity在屏幕旋转之后ViewModel中的数据不丢失,内部有一个Map来保存所有的ViewModel

ViewModelProvider.Factory乍一看就知道是一个工厂类,其实它是ViewModelProvider内部接口,只有一个create(clazz)方法,该方法主要就是用来创建ViewModel对象,我们常用的有NewInstannceFactoryAndroidViewModelFactory

ViewModelProvider则是负责如果中转作用,将factory生成的viewmodel存到store中,如果store中已经有值,直接返回已存在的对象。

ViewModelStore

作用上面已经提过,直接看源码吧~

public class ViewModelStore {

    // 用于保存ViewModel对象的集合,key是ViewModel类的canonicalName,所以key是唯一的
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore源码还是很简单的,就类似于一个数据存储类,一个ViewModel对象只会存储一份。

ViewModelProvider.Factory

Factory接口只有一个方法,我们下面结合NewInstanceFactory源码一起看下实现原理。

public interface Factory {
    /**
     * Creates a new instance of the given `Class`.
     *
     * @param modelClass a `Class` whose instance is requested
     * @return a newly created ViewModel
     */
    public fun <T : ViewModel> create(modelClass: Class<T>): T
}

public open class NewInstanceFactory : Factory {
    @Suppress("DocumentExceptions")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return try {
            modelClass.newInstance()
        } catch (e: InstantiationException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        } catch (e: IllegalAccessException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        }
    }
}

NewInstanceFactory是官方默认的一种实现,create()方法很简单,就是根据Class实例化出一个对象。

到此我们知道Factory可以实例化一个ViewModel对象,然后存到ViewModelStore中,最后我们看看ViewModelProvider是如何将这两个结合起来的。

ViewModelProvider

在这我们先看下如果通过ViewModelProvider来获取一个ViewModel对象,常用的写法:

val viewModel = ViewModelProvider(
            viewModelStore,
            ViewModelProvider.NewInstanceFactory.instance
        ).get(Vm::class.java)
  • 创建ViewModelProvider对象需要两个参数,这两个我们在上面都解析过,这里viewModelStore是通过Activity中的getViewModelStore()获取而来,一个Activity维护了一个ViewModelStore对象,并且在屏幕旋转重建之后此对象不变,可以了解下NonConfigurationInstances的原理。
  • 通过ViewModelProvider.get(Clazz)得到ViewModel实例,下面重点看下这个方法。
@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
    val canonicalName = modelClass.canonicalName
        ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
    // 1、拼接key值,调用两个参数的get()方法
    return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    // 2、先看看ViewModelStore中是否存在此ViewModel
    var viewModel = store[key]
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    // 3、不存在调用Factory.create()去实例化
    viewModel = if (factory is KeyedFactory) {
        factory.create(key, modelClass)
    } else {
        factory.create(modelClass)
    }
    // 4、存入ViewModelStore中
    store.put(key, viewModel)
    return viewModel
}

ViewModelProvider.get()方法逻辑十分清晰,上述注释我分了4步解释,这里就不再啰嗦了~

文档

官方文档

ViewModel版本