问题背景
存在一个场景,在Acitivity1可以跳转到Activity2,但是两个Activty之间希望能共享数据
提出假设的手段
可以定义一个ViewModel,让这两个Activity去共享这个ViewModel
存在的问题
根据不同的LifecycleOwner创建出来的ViewModel是不同的实例,所以在两个不同的Activity之间无法创建同一个ViewModel对象
问题分析
先来梳理一下一个正常的ViewModel是怎么被构造出来的:
- ViewModel是由ViewModelFactoty负责构造出来
- 构造出来之后,存储在ViewModelStore里面 但是问题是ViewModelStore是 和 (宿主Activity或者Fragment)是一一对应的关系 具体代码如下
@MainThread
public inline fun <reified VM : ViewModel> Fragment.viewModels(
noinline ownerProducer: () -> ViewModelStoreOwner = { this },
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val owner by lazy(LazyThreadSafetyMode.NONE) { ownerProducer() }
return createViewModelLazy(
VM::class,
{ owner.viewModelStore },
{
(owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras
?: CreationExtras.Empty
},
factoryProducer ?: {
(owner as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory
?: defaultViewModelProviderFactory
})
}
看到上面的代码第9行,viewModelStore和owner是对应关系,所以原则上根据不同LifecycleOwner无法构造出同一个ViewModel对象
解决思路
-
无法在不同的LifecycleOwner之间共享ViewMode对象的原因是:ViewModel的存储方ViewModelStore是和LifecycleOwner绑定,那如果可以解开这一层绑定关系,理论上就可以实现共享;
-
另外我们需要定义ViewModel的销毁时机:
我们来模拟一个场景:由Activty1跳转到Activity2,然后两个Activity共享同一个ViewModel,两个activity都要拿到同一个ViewModel的实例,那这个时候ViewModel的销毁时机应该是和Acitivity1的生命周期走,也就是退出Activity1(等同于Activity1走onDestroy)的时候,去销毁这个ViewModel。
所以按照这个思路走,ViewModel需要在activity1中被创建出来,并且保存在一个特定的ViewModelStore里面,要保证这个ViewModelStore可以被这两个Activity共享;
然后等到Activity2取的时候,就直接可以从这个ViewModelStore把这个ViewModel取出来;
最后在Activity1进到destroy的时候,销毁这个ViewModel
具体实现
重写一个ViewModelProvider实现如下功能点:
- 把里面的ViewModelStore定义成一个单例供所有的LifecycleOwner共享
- 定义ViewModel的销毁时机: LifecycleOwner走到onDestroy的时机
// 需要放到lifecycle这个包,否则访问不到ViewModelStore
package androidx.lifecycle
class GlobalViewModelProvider(factory: Factory = NewInstanceFactory()) :
ViewModelProvider(globalStore, factory) {
companion object {
private val globalStore = ViewModelStore()
private val globalLifecycleMap = HashMap<String, MutableSet<Lifecycle>>()
private const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
}
@MainThread
fun <T: ViewModel> get(lifecycle: Lifecycle, modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
return get(lifecycle, "$DEFAULT_KEY:$canonicalName", modelClass)
}
@MainThread
fun <T: ViewModel> get(lifecycle: Lifecycle, key: String, modelClass: Class<T>): T {
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
throw IllegalStateException("Could not get viewmodel when lifecycle was destroyed")
}
val viewModel = super.get(key, modelClass)
val lifecycleList = globalLifecycleMap.getOrElse(key) { mutableSetOf() }
globalLifecycleMap[key] = lifecycleList
if (!lifecycleList.contains(lifecycle)) {
lifecycleList.add(lifecycle)
lifecycle.addObserver(ClearNegativeVMObserver(lifecycle, key, globalStore, globalLifecycleMap))
}
return viewModel
}
private class ClearNegativeVMObserver(
private val lifecycle: Lifecycle,
private val key: String,
private val store: ViewModelStore,
private val map: HashMap<String, MutableSet<Lifecycle>>,
): LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
val lifecycleList = map.getOrElse(key) { mutableSetOf() }
lifecycleList.remove(lifecycle)
if (lifecycleList.isEmpty()) {
store.put(key, null)
map.remove(key)
}
}
}
}
}
具体使用
@MainThread
inline fun <reified VM: ViewModel> LifecycleOwner.sharedViewModel(
viewModelClass: Class<VM> = VM::class.java,
noinline keyFactory: (() -> String)? = null,
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null,
): Lazy<VM> {
return SharedViewModelLazy(
viewModelClass,
keyFactory,
{ this },
factoryProducer ?: { ViewModelProvider.NewInstanceFactory() }
)
}
@PublishedApi
internal class SharedViewModelLazy<VM: ViewModel>(
private val viewModelClass: Class<VM>,
private val keyFactory: (() -> String)?,
private val lifecycleProducer: () -> LifecycleOwner,
private val factoryProducer: () -> ViewModelProvider.Factory,
): Lazy<VM> {
private var cached: VM? = null
override val value: VM
get() {
return cached ?: kotlin.run {
val factory = factoryProducer()
if (keyFactory != null) {
GlobalViewModelProvider(factory).get(
lifecycleProducer().lifecycle,
keyFactory.invoke(),
viewModelClass
)
} else {
GlobalViewModelProvider(factory).get(
lifecycleProducer().lifecycle,
viewModelClass
)
}.also {
cached = it
}
}
}
override fun isInitialized() = cached != null
}
场景使用
val vm : MainViewModel by sharedViewModel()
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题