如何让不同Activity之间共享同一个ViewModel

问题背景

存在一个场景,在Acitivity1可以跳转到Activity2,但是两个Activty之间希望能共享数据

提出假设的手段

可以定义一个ViewModel,让这两个Activity去共享这个ViewModel

存在的问题

根据不同的LifecycleOwner创建出来的ViewModel是不同的实例,所以在两个不同的Activity之间无法创建同一个ViewModel对象

问题分析

先来梳理一下一个正常的ViewModel是怎么被构造出来的:

  1. ViewModel是由ViewModelFactoty负责构造出来
  2. 构造出来之后,存储在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对象

解决思路

  1. 无法在不同的LifecycleOwner之间共享ViewMode对象的原因是:ViewModel的存储方ViewModelStore是和LifecycleOwner绑定,那如果可以解开这一层绑定关系,理论上就可以实现共享;

  2. 另外我们需要定义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实现如下功能点:

  1. 把里面的ViewModelStore定义成一个单例供所有的LifecycleOwner共享
  2. 定义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()

如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料


在这里插入图片描述
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值