Android 跨Activity(Fragment)共享ViewModel無(wú)反射實(shí)現(xiàn)

第一版 實(shí)現(xiàn)中使用到了基于注解的方式去標(biāo)記作用域,再通過(guò)反射注入對(duì)象的方式實(shí)現(xiàn),這種方式不僅效率低使用也不太靈活,有個(gè)很大的弊端無(wú)法使用自定義ViewModelFactory。

所以在第二版優(yōu)化中將拋棄注解反射的方式,實(shí)現(xiàn)靈感來(lái)源于fragment-ktx中的viewModles()擴(kuò)展方法。由第一版中所講到的共享原理可知我們只需要保證從同一個(gè)Stroe中去獲取ViewModel,那么同類(lèi)型的ViewModel被獲取到時(shí)始終都會(huì)時(shí)同一個(gè)。

首先我們需要引入擴(kuò)展包:

implementation 'androidx.fragment:fragment-ktx:1.2.4'

并且在App模塊中的Gradle配置虛擬機(jī)版本,否則無(wú)法使用內(nèi)連函數(shù)

kotlinOptions{
jvmTarget = "1.8"
}

使用擴(kuò)展方法配合Lazy實(shí)現(xiàn)viewModel注入,此種實(shí)現(xiàn)方式特點(diǎn)可感知生命周期,如果同一個(gè)key標(biāo)記的viewModel界面都被銷(xiāo)毀了則會(huì)自動(dòng)清除ViewModelStore不會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題。

復(fù)制ShareViewModel.kt文件到項(xiàng)目中

package com.cj.customwidget.page.viewmodelv2

import androidx.lifecycle.*

val vMStores = HashMap<String, VMStore>()

inline fun <reified VM : ViewModel> LifecycleOwner.shareViewModels(
    scopeName:String,
    factory: ViewModelProvider.Factory?=null
):Lazy<VM> {
    val store: VMStore
    if (vMStores.keys.contains(scopeName)) {
        store = vMStores[scopeName]!!
    } else {
        store = VMStore()
        vMStores[scopeName] = store
    }
    store.register(this)
    return ViewModelLazy(VM::class,{store.viewModelStore},{factory?:ViewModelProvider.NewInstanceFactory()})
}

class VMStore : ViewModelStoreOwner {

    private val bindTargets = ArrayList<LifecycleOwner>()
    private var vmStore: ViewModelStore? = null

    fun register(host: LifecycleOwner) {
        if (!bindTargets.contains(host)) {
            bindTargets.add(host)
            host.lifecycle.addObserver(object : LifecycleEventObserver {
                override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        host.lifecycle.removeObserver(this)
                        bindTargets.remove(host)
                        if (bindTargets.isEmpty()) {//如果當(dāng)前商店沒(méi)有關(guān)聯(lián)對(duì)象,則釋放資源
                            vMStores.entries.find { it.value == this@VMStore }?.also {
                                vmStore?.clear()
                                vMStores.remove(it.key)
                            }
                        }
                    }
                }
            })
        }
    }

    override fun getViewModelStore(): ViewModelStore {
        if (vmStore == null)
            vmStore = ViewModelStore()
        return vmStore!!
    }
}

使用方式,使用懶加載的方式并傳入分組key即可,相同的key會(huì)共享同一個(gè)ViewModelStore對(duì)象。

class VM1Activity : AppCompatActivity() {
     val vm:ViewModel1 by shareViewModels("Lucas")
}

class VM2Activity : AppCompatActivity() {
     val vm:ViewModel1 by shareViewModels("Lucas")
}

class VMFragment: Fragment() {
     val vm:ViewModel1 by shareViewModels("Lucas")
}

運(yùn)行查看多個(gè)界面間對(duì)象地址是否相同

image.png

當(dāng)然我們可以自己通過(guò)定義ViewModelProvider.Fractory的方式來(lái)決定創(chuàng)建那些VIewModel以及對(duì)ViewModel進(jìn)行構(gòu)造穿參。
如果沒(méi)有傳入ViewModelProvider.Fractory內(nèi)部會(huì)使用默認(rèn)的工廠進(jìn)行創(chuàng)建ViewModel.

class ViewModel2(val param:String):ViewModel() {

    fun test(){
        "aaaaa".p()
    }
}
class VM1Activity : AppCompatActivity() {
     val vm:ViewModel2 by shareViewModels("lucas",object :ViewModelProvider.Factory{
         override fun <T : ViewModel?> create(modelClass: Class<T>): T {
             return ViewModel2("哈哈") as T
         }
     })
}

項(xiàng)目地址:https://github.com/LucasDevelop/CustomView. 中的View model 案例

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容