【问题标题】:Share ViewModel between fragments that are in different Activity在不同 Activity 中的 Fragment 之间共享 ViewModel
【发布时间】:2017-06-19 22:51:13
【问题描述】:

我有一个名为 SharedViewModel 的 ViewModel:

public class SharedViewModel<T> extends ViewModel {

    private final MutableLiveData<T> selected = new MutableLiveData<>();


    public void select(T item) {
        selected.setValue(item);
    }

    public LiveData<T> getSelected() {
        return selected;
    }
}

我已经根据 Google 的 Arch ViewModel 参考页面上的 SharedViewModel 示例实现了它:

https://developer.android.com/topic/libraries/architecture/viewmodel.html#sharing_data_between_fragments

一个活动中的两个或多个片段需要相互通信是很常见的。这绝不是微不足道的,因为两者 片段需要定义一些接口描述和所有者 活动必须将两者结合在一起。此外,这两个片段必须 处理其他片段尚未创建或未创建的情况 可见。

我有两个片段,分别称为 ListFragmentDetailFragment

到目前为止,我在一个名为MasterActivity 的活动中使用了这两个片段,并且一切正常。

我在 ListFragment 中获得了 ViewModel,选择了要在 DetailFragment 上使用的值。

mStepSelectorViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

但是,现在,在某些情况下,我需要将 ListFragment(不同设备配置的布局)添加到名为 DetailActivity 的不同活动中。有没有办法类似于上面的例子?

【问题讨论】:

  • 你正在与框架作斗争。如果它转到另一个名为 DetailActivity 的活动,则为您的 Detail Activity 提供一个 ViewModel。 ViewModel 不仅仅是“可以比配置更改更持久的魔法对象”。它们是您的视图和模型之间的粘合剂。如果您有不同的活动,您的视图现在不同,因此需要一个新的视图模型。如果您像谷歌期望的那样分离您的关注点,那么有权访问数据的“存储库”可以/将由这些视图模型共享,但它们是不同的实体。
  • 如果你要传递给一个新的 Activity,你不应该只用你的对象去Parcelable 吗?
  • 实现一个接口来监听在你的活动中选择了哪些列表项,包括 ListFragment。然后在 ListFragment 中,在 onAttach 事件中获取接口。使用 findFragmentByTag 将选中的 item 传递给 MasterActivity 中的 DetailFragment 并传递给其他 Activity 中的 DetailActivity。

标签: android android-fragments android-activity viewmodel android-architecture-lifecycle


【解决方案1】:

有点晚了,但您可以使用共享的ViewModelStore 完成此操作。片段和活动实现ViewModelStoreOwner 接口。在这些情况下,片段每个实例都有一个存储,活动将其保存在一个静态成员中(我想这样它就可以在配置更改后继续存在)。

回到共享的ViewModelStore,假设您希望它成为您的应用程序实例。您需要您的应用程序来实现ViewModelStoreOwner

class MyApp: Application(), ViewModelStoreOwner {
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

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

然后在您知道需要在活动边界之间共享 ViewModel 的情况下,您可以这样做。

val viewModel = ViewModelProvider(myApp, viewModelFactory).get(CustomViewModel::class.java)

所以现在它将使用您应用中定义的商店。这样您就可以共享 ViewModel。

非常重要。因为在这个例子中ViewModels 存在于您的应用程序实例中,当使用它们的片段/活动被破坏时,它们不会被破坏。因此,您必须将它们链接到将使用它们的最后一个片段/活动的生命周期,或者手动销毁它们。

【讨论】:

  • 哇!我从来没有想过这个。如果我采用这种方法,有什么需要注意的吗?
  • 我需要知道什么问题?
  • 只是这取决于您决定在哪里拥有ViewModelStore,您需要手动清除存储以避免内存泄漏。如果您按照我的回答中的示例(将其存储在 App 实例中),您将需要这样做。
  • @mikehc 谢谢。我把 ViewModelStore 放在 App 实例中,就像你回答的那样。我有两个活动 A 和 B。A 永远不会被破坏(除非关闭应用程序)并且 B 经常打开和关闭。那么如何清除 ViewModelStore 呢?我在 B (application as MyApplication).viewModelStore.clear() 的 onDestroy 方法中做了,但是当我再次启动 B 活动时,viewModel 不起作用
  • @leotesta 听起来问题可能出在您的 ViewModelFactory 上。但这一切都取决于您的实施。我建议您在分享一点您的实现时针对该问题创建一个新问题。
【解决方案2】:

好吧,我为此创建了一个名为Vita的库,您可以在活动之间共享ViewModels,甚至可以在具有不同宿主活动的片段之间共享:

val myViewModel = vita.with(VitaOwner.Multiple(this)).getViewModel<MyViewModel>()

以这种方式创建的ViewModel 一直存在,直到它的最后一个LifeCycleOwner 被销毁。

您还可以创建具有应用程序范围的ViewModels:

val myViewModel = vita.with(VitaOwner.None).getViewModel<MyViewModel>()

并且这种ViewModel会在用户关闭应用时被清除

试一试,请告诉我您的反馈: https://github.com/FarshadTahmasbi/Vita

【讨论】:

    【解决方案3】:

    您可以使用工厂来制作视图模型,这个因素将返回视图模型的单个对象。如:

    class ViewModelFactory() : ViewModelProvider.Factory {
    
    override fun create(modelClass: Class): T {
        if (modelClass.isAssignableFrom(UserProfileViewModel::class.java)) {
        val key = "UserProfileViewModel"
        if(hashMapViewModel.containsKey(key)){
            return getViewModel(key) as T
        } else {
            addViewModel(key, UserProfileViewModel())
            return getViewModel(key) as T
        }
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
    
    companion object {
        val hashMapViewModel = HashMap<String, ViewModel>()
        fun addViewModel(key: String, viewModel: ViewModel){
            hashMapViewModel.put(key, viewModel)
        }
        fun getViewModel(key: String): ViewModel? {
            return hashMapViewModel[key]
        }
    }
    }
    

    活动中:

    viewModelFactory = Injection.provideViewModelFactory(this)
    
    // Initialize Product View Model
    userViewModel = ViewModelProviders.of(this, viewModelFactory).get(
    UserProfileViewModel::class.java)`
    

    这将只提供一个 UserProfileViewModel 对象,您可以在活动之间共享它。

    【讨论】:

    • “Injection.provideViewModelFactory(this)”这一行怎么样,如何让它工作?
    • Injection 是自定义类,负责提供 ViewModelFactory 的实例。这里是类:对象注入 { fun provideViewModelFactory(context: Context): ViewModelFactory { return ViewModelFactory() } }
    • 查看 herriojr github.com/googlesamples/android-architecture-components/issues/… 的评论,为什么这可能不是解决方案。
    【解决方案4】:

    我认为我们仍然对 Android 上的 MVVM 框架感到困惑。 对于另一个活动,不要混淆,因为它必须相同,为什么?

    如果它具有相同的逻辑(即使逻辑在其他有用的类中仍然可以是抽象的),或者如果 XML 中的视图几乎相同,这是有意义的。

    让我们举个简单的例子:

    我创建了一个名为vmA的ViewModel和一个名为A的活动,我需要用户的数据,我将把存储库插入到用户的vmA中。

    现在,我需要另一个需要读取用户数据的活动, 我创建了另一个名为 vmB 的 ViewModel,在其中我将调用用户存储库。 如前所述,存储库始终相同。

    已经建议的另一种方法是使用 Factory 的实现创建同一 ViewModel 的 N 个实例。

    【讨论】:

      【解决方案5】:

      如果您想要一个由所有活动(而不是某些活动)共享的 ViewModel, 那么为什么不存储您想要存储在该 ViewModel 中的内容 在您的应用程序类中?

      在上一届 Google I/O 上呈现的趋势似乎是放弃活动的概念,转而支持具有大量 Fragment 的单活动应用程序。 ViewModels 是删除大量接口的方法,接口的活动以前必须实现。 因此,这种方法不再导致大型和不可维护的活动。

      【讨论】:

      • 我同意您关于减少应用程序中的活动数量的观点,尽管仍然存在需要活动的情况。例如设置活动(带有设置片段),并且您希望在设置更改后在两个视图模型之间共享视图模型
      • 不,设置是持久的。在设置之外使用的 ViewModel 应该随着底层数据的变化而自我更新。设置不会更改临时数据,例如尚未发送或保存的半打字博客帖子。他们更改永久设置。这将是 Room 或属性集的包装器返回的 Livedata 的工作
      • 我理解您的观点,对属性集进行包装是有意义的。我唯一不明白的是你如何在这个包装器和需要更改的视图模型之间建立联系。最后,您将需要使用 ViewModelProviders.of(>, viewModelFactory) 来获取相同的 viewmodel 实例。
      • 谢谢,我最终以使用这个 instaed android.jlelse.eu/… 结束。我认为,如果您最终需要使用来自不同活动的不同视图模型,那么您的应用程序设计中可能会有一个流程。
      【解决方案6】:

      这里是a link

      希望对您有所帮助。 O(∩_∩)O~

      另外:

      1) 代码的灵感来自smart pointer in c++

      2) 当没有活动或片段引用ShareViewModel时,它将被自动清除。 ShareViewModel # onShareCleared() 函数将同时被调用! 您无需手动销毁它们!

      3) 如果您使用 dagger2 注入 ViewModelFactory 以共享视图模型
      在两个活动(可能三个)之间,这里是sample

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-10
        • 2020-03-25
        • 1970-01-01
        • 2021-06-27
        • 1970-01-01
        • 1970-01-01
        • 2017-01-12
        相关资源
        最近更新 更多