View Model does not have to inherit from ViewModel

Recommended practices from Google generally include using ViewModel as the base class for View Models (the ones in MVVM). The ViewModel is a great thing for saving anything in case of screen rotation, be it View Model, Presenter, or Router. But is it possible to get all the pivot survival benefits without having to inherit from the ViewModel directly?





- ? :





  • ViewModel — . .





  • ViewModel Android. View Model Kotlin Multiplatform?





  • View Model, CoroutineScope, .





ViewModel

, - , , .





. ViewModel. ViewModel , Fragment, , . 





:





ViewModel. , , View Model MVVM. ? ViewModel. - , MVVM? ViewModel.





. ViewModel , . , , , VM ( ), .





:





ViewModel ViewModelProvider.get(), 'onCleared()' . . , VM , , DI ViewModelProvider.Factory . , .





CoroutineScope. ViewModel CoroutineScope, ktx . , , , .





:





SRP , ViewModel , CoroutineScope. . , CoroutineScope ViewModel . , VM, , . CoroutineScope? Hilt, , extension function KTX-.





CoroutineScope ViewModel. View Model ( ), View CoroutineScope: - «». , .





SavedStateHandle. SavedStateHandle «» . , . SavedStateHandle . .





, ViewModel

— VM . , API , . 





, :





inline fun <reified T: Any> ViewModelStoreOwner.getOrCreatePersisted(create: () -> T): T
      
      



? — ViewModel :





class PersistentStorage : ViewModel() {

    private val persisted = mutableMapOf<Class<*>, Any>()

    fun <T: Any> getOrCreate(clazz: Class<T>, create: () -> T) =
            persisted.getOrPut(clazz) { create.invoke() } as T
}

inline fun <reified T : Any> ViewModelStoreOwner.getOrCreatePersisted(noinline create: () -> T): T =
        ViewModelProvider(this).get<PersistentStorage>().getOrCreate(T::class.java, create)
      
      



, . . , ViewModel:





val myVM = getOrCreatePersisted { MyMV(params) }
      
      



. , API ViewModel : , VM .





, ( ), . ViewModel ( ) , . 





:





interface PersistentLifecycle {
    fun addOnClearResourcesListener(listener: () -> Unit)
}

class PersistentLifecycleImpl : ViewModel(), PersistentLifecycle {

    private val listeners = mutableListOf<() -> Unit>()

    override fun addOnClearResourcesListener(listener: () -> Unit) {
        listeners.add(listener)
    }

    override fun onCleared() {
        super.onCleared()
        for (listener in listeners) {
            listener.invoke()
        }
        listeners.clear()
    }
}

fun ViewModelStoreOwner.persistentLifecycle(): PersistentLifecycle =
        ViewModelProvider(this).get<PersistentLifecycleImpl>()
      
      



, . , ViewModel, MVVM :)





. «» , API:





class MyViewModel(persistentLifecycle: PersistentLifecycle) {
    init {
        persistentLifecycle.addOnClearResourcesListener {
            // clean resources
        }
    }
}
      
      



CoroutineScope. C . CoroutineScope , . , VM:





class MyViewModel(
        private val coroutineScope: CoroutineScope
) {

    fun onSomethingClick() {
        coroutineScope.launch { 
            // do something 
        }
    }
}
      
      



ViewModel:





class CoroutineScopeViewModel : ViewModel()

fun ViewModelStoreOwner.persistentCoroutineScope() =
        ViewModelProvider(this).get<CoroutineScopeViewModel>().viewModelScope
      
      



SavedStateHandle. . API, Android-.





:





class MyViewModel(
        stateHelper: SavedStateHelper
) {
    private var screenId: String by stateHelper.savedState("screenId", default = "")
}
      
      



SavedStateHelper :





interface SavedStateHelper {
    fun <T: Any, VM : Any> savedState(key: String, default: T): ReadWriteProperty<VM, T>
}
      
      



:





class SavedStateHelperImpl(
        private val stateHandle: SavedStateHandle
) : ViewModel(), SavedStateHelper {

    override fun <T: Any, VM : Any> savedState(key: String, default: T): ReadWriteProperty<VM, T> {
        return PersistentStateDelegate(stateHandle, key, default)
    }
}

fun Fragment.savedStateHelper(): SavedStateHelper =
        ViewModelProvider(this, SavedStateViewModelFactory(requireActivity().application, this))
                .get<SavedStateHelperImpl>()

private class PersistentStateDelegate<T: Any>(
        private val holder: SavedStateHandle,
        private val key: String,
        private val default: T
) : ReadWriteProperty<Any, T> {

    override fun getValue(thisRef: Any, property: KProperty<*>): T {
        return holder.get<T>(getKey(thisRef))
            ?: default.also { setValue(thisRef, property, it) }
    }

    override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
        holder.set(getKey(thisRef), value)
    }

    private fun getKey(thisRef: Any) = "${thisRef.javaClass.name}__$key"
}
      
      



"${thisRef.javaClass.name}__$key". , , - .





, SavedStateHandle. Bundle Parcelable View Model, .





API View Model, . .





View Model :





val viewModel = getOrCreatePersisted {
    MyViewModel(savedStateHelper(), persistentCoroutineScope(), persistentLifecycle())
}
      
      



, — Constructor Injection. DI-, , .





UPD: , . , .





, , Wrike Android- . , . , Flutter . , :). ,








All Articles