如何使用Koin DI在不同Activity之间共享同一个ViewModel实例?

17

我在Kotlin中使用Koin库进行依赖注入。

Koin提供了by viewmodel()方法用于获取ViewModel实例,by sharedViewModel()方法用于在Fragment中获取相同的ViewModel实例。

我该如何在Activity中获取相同的ViewModel实例?我没有找到任何实现方式。


4
这不受“ViewModel”的支持。 - CommonsWare
这可以通过使用ViewModel Factory实现,但我没有找到在koin中提供自定义工厂类的方法。 - Sahil Arora
5个回答

7

在模块声明中,必须使用single{}而不是viewModel{}

single { SharedViewModel() }

而且,您可以在视图中使用viewModel()。

View1

private val viewModel: SharedViewModel by viewModel()

视图2

private val viewModel: SharedViewModel by viewModel()

但是您必须在视图启动时加载模块,方法是:

loadKoinModules(module1)

重要的是,在销毁视图时必须卸载模块。

unloadKoinModules(mainModule)

因此,当卸载模块时,您的单例ViewModel将被销毁。

#编辑

现在,您可以使用sharedViewModel声明。


8
我认为这不是一个好主意,因为“ViewModel”应该绑定到“LifecycleOwner”(“ViewModelProvider”会为我们创建它,这就是为什么我们通过它获取“ViewModel”实例),所以当“LifecycleOwner”被销毁时,它的“ViewModel”也会被销毁。至于“single { ViewModel() }”,它将创建一个单例的“ViewModel”,它没有绑定到任何“LifecycleOwner”,所以不能正确地销毁它。此外,单例“ViewModel”违反了“ViewModel”的概念。相反,可以使用“Repositories”来在两个“ViewModel”之间共享数据。 - Yamashiro Rion
如果你这样做,onCleared将永远不会被调用。 - EpicPandaForce
Koin在单一-ViewModel方面遇到了一些问题,我成功地通过使用一些替代方法和Koin来解决了我的问题,你可以在https://dev59.com/aLfna4cB1Zd3GeqPy8qy#71156479找到答案。 - Willey Hute
这种方法不起作用。 - Sultan
OP问的是在活动之间,但答案是在片段之间。 - Adi

7

经过对架构层面的研究或讨论以及报告和 Github Koin 上的问题,我找到了解决方案。

在这种情况下,我们应该将需要在多个活动之间共享的状态/数据保存到 Repository 中,而不是 ViewModel,并且两个或多个不同的 ViewModels 可以访问保存在单个 Repository 实例中的相同状态/数据。

请点击此处查看图片


1
这不是正确的答案,因为ViewModel根据定义可以在视图之间共享。Google一直在鼓励MVVM设计,正是因为它使我们不需要像这个答案所建议的那样做。MVVM架构是一种声明性和反应式的方法。每次需要共享数据时从数据库中插入和恢复数据对于应用程序来说成本更高。在我看来,这个答案是关于MVP方法而不是MVVM。如果你想要更加命令式和繁琐的东西,考虑将你的架构移动到MVP或MVC。 - Jean Patricio
1
你可能已经解决了你的问题,但这并没有回答“如何在不同 Activity 之间使用 Koin DI 共享同一个 ViewModel 实例”的问题。 - Some random IT boy

2
我建议将应用程序作为ViewModelStoreOwner,并使用应用程序作为owner来注入viewModels。所需的代码如下:
class App : Application(), ViewModelStoreOwner {
    private val mViewModelStore = ViewModelStore()

    override fun getViewModelStore(): ViewModelStore {
        return mViewModelStore
    }
}

你可以定义一些扩展来轻松注入视图模型。

val Context.app: App
    get() = applicationContext as App

inline fun <reified T : ViewModel> Context.appViewModel(
    qualifier: Qualifier? = null,
    noinline state: BundleDefinition? = null,
    noinline parameters: ParametersDefinition? = null
): Lazy<T> {
    return lazy(LazyThreadSafetyMode.NONE) {
        GlobalContext.get().getViewModel(qualifier, state, { ViewModelOwner.from(app, null) }, T::class, parameters)
    }
}

inline fun <reified T : ViewModel> Fragment.appViewModel(
    qualifier: Qualifier? = null,
    noinline state: BundleDefinition? = null,
    noinline parameters: ParametersDefinition? = null
): Lazy<T> {
    return lazy(LazyThreadSafetyMode.NONE) {
        GlobalContext.get().getViewModel(qualifier, state, { ViewModelOwner.from(requireContext().app, null) }, T::class, parameters)
    }
}


您可以这样注入您的viewModel:

class MainActivity : AppCompatActivity() {
    private val mAppViewModel: AppViewModel by appViewModel()
}

这种解决方案的优点是,您不需要重新创建视图模型。如果您决定在应用程序重新启动之间保存状态,则还可以轻松将应用程序设置为SavedStateRegistryOwner,并使用SavedStateHandle从viewModel内部保存/恢复状态,现在绑定到进程生命周期。

1
如何重写 getViewModelStore() 方法? - Nurseyit Tursunkulov
抱歉,我在答案中添加了缺失的getViewModelStore()覆盖。 - Catalin
重写此方法已不再支持,该方法将在未来版本的ComponentActivity中被标记为<code>final</code>。:shrug: - childno͡.de

1
您需要阅读更多关于ViewModel的内容,以更好地理解它。 https://developer.android.com/topic/libraries/architecture/viewmodel ViewModel连接到您的Activity
所以您只能在其Fragments之间共享活动的ViewModel
这就是koinsharedViewModel的含义。
如果使用相同的contextViewModel Factory,则sharedViewModel是相同的。
在Android中,可以通过Intent共享任何数据,没有其他方式,
或者您可以保存一些静态/全局数据并在Activities之间共享它们。

我知道ViewModel模式,但是使用ViewModel自定义工厂,我们可以在不同的活动中共享单个ViewModel实例,所以我的问题是,在koin中是否有提供自定义ViewModel工厂的方法。请参见此处:https://developer.android.com/reference/android/arch/lifecycle/ViewModelProvider.Factory - Sahil Arora
@SahilArora 好的。如果你在活动之间共享一些数据,请注意无论如何你都会遇到以下问题:
  1. 它必须是静态的。
  2. 你将会有内存泄漏问题。
  3. Android 创建意图(Intents),如果你想在活动之间进行自定义数据提供,你将会遇到静态(全局)和当然是内存泄漏的情况。
- Hayk Melkonyan

-1

我知道这已经很晚了,但你可以尝试一下这个方法: 如果你正在扩展一个BaseViewModel,你需要将baseViewModel声明为single,然后在你的相应活动中注入BaseViewModel。 实际例子:

val dataModule = module {
    single { BaseViewModel(get(), get()) }
}

在您的ViewModel中

class LoginViewModel(private val param: Repository,
                            param1: Pref,
                            param2: Engine) : BaseViewModel(param1, param2)

然后在你的活动类中

val baseViewModel: BaseViewModel by inject()

希望这能帮助到某个人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接