在 第一版 實(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ì)象地址是否相同
當(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 案例