【问题标题】:Sharing Data between ViewModels在 ViewModel 之间共享数据
【发布时间】:2019-07-02 10:43:53
【问题描述】:

我的项目中有一个复杂的屏幕,我要拆分多个片段。我正在尝试遵循这些类的 MVVM 架构,因此哪个片段有自己的 ViewModel 和 Contract 类。

问题是所有 ViewModel 都需要相同的对象实例(我们称之为Book)来执行 Room 事务。

是否有正确的方法在 ViewModel 之间共享数据(或 LiveData)?我知道 Shared ViewModel 的概念,但我不知道我是否可以将它应用到这个案例中。我也考虑过使用MediatorLiveData,但也没有找到好的方法。

我正在考虑创建一个带有LiveData<Book>(或Rx Subject<Book>)的类(我们称之为BookObservableProvider),其中每个ViewModel 注入相同的实例并加载/更新始终相同的值。

这是一个好方法吗?

【问题讨论】:

  • 每个活动使用单个 ViewModel,因此您的 Fragment 将自动共享相同的 ViewModel。
  • @RoshaanFarrukh 拆分类的目的是不让 ViewModel 膨胀,因为 ViewModel 将来会有更多的方法。
  • 你找到好方法了吗?目前,我正在通过其中一个 SharedViewModel 中的存储库获取数据,然后在我的活动中观察这些数据。在观察函数调用中,我还将它更新为其他 ViewModel 实例,我也在我的所有片段中访问它们。
  • @IgorEscodro 我想我也有同样的问题:stackoverflow.com/q/60796010/8258130你解决了你的问题吗?请分享
  • @nulldroid 是的。那也可以!如果你想到了,你能在这里查看我的帖子吗stackoverflow.com/q/60796010/8258130

标签: android android-livedata android-viewmodel android-mvvm


【解决方案1】:

您应该在片段/活动之间共享这些数据(可能使用 Intent 进行活动),而不是由其他 ViewModel 处理这些数据

【讨论】:

  • 是的,这是一种方法。但问题是让 View 处理数据以及保持所有数据同步的方法。
【解决方案2】:

答案和往常一样,视情况而定。 如果您的问题背后的原因是房间访问,那么建议有一个处理所有数据库访问的 DataRepository 类,您只需将该存储库单例传递给每个 AndroidViewModel

mRepository = ((MainApp) application).getRepository();

在 MainApp 中:

public DataRepository getRepository() {
    return DataRepository.getInstance(getDatabase(), mAppExecutors);
}

以及存储库:

public class DataRepository {

    private static DataRepository sInstance;
    private MediatorLiveData<String> mObservableString;

    private DataRepository(final AppDatabase database, final AppExecutors executors) {
        mObservableString.addSource(database.myDao().loadString(),
            mString -> {
                if (database.getDatabaseCreated().getValue() != null) {
                    mObservableString.postValue(mString);
                }
            });
    }

    public static DataRepository getInstance(final AppDatabase database, final AppExecutors executors) {
        if (sInstance == null) {
            synchronized (DataRepository.class) {
                if (sInstance == null) {
                    sInstance = new DataRepository(database, executors);
                }
            }
        }
        return sInstance;
    }
    
    // and then your access methods
    
    public LiveData<String> getString() {
        return mObservableString;
    }

如果您想更改引用(源),建议在存储库中使用 MediatorLivedata。否则,一个普通的 LiveData 就可以完成这项工作。

关于 ViewModel:

理论上每个片段都有自己的视图模型。如果您通过使用 requireActivity() 作为参考来获取它,您可以在任何地方获取每个 ViewModel 并因此共享它。

举个例子:

    viewModelA = new ViewModelProvider(requireActivity()).get(ViewModelA.class);
    viewModelB = new ViewModelProvider(requireActivity()).get(ViewModelB.class);

你可以在每个 Fragment 中调用它并获得相同的 ViewModel 实例。如果 DataRepository 设置对您来说似乎有点过头了,请创建一个具有 Room 访问权限的 ViewModel 并从每个 Fragment 加载它。

【讨论】:

    【解决方案3】:

    我也面临同样的问题。但是,如果您对各种片段没有不同的视图模型,或者设计不需要为各种片段使用不同的视图模型,您可以在整个活动(所有其他片段)之间共享一个片段,它们都会共享相同的数据实例。

    点击此链接了解更多https://developer.android.com/guide/fragments/communicate

    您需要做的是确保所有片段以相同的上下文启动视图模型(主视图模型)。

    public class FilterFragment extends Fragment {
    private ListViewModel viewModel;
    
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);
        viewModel.getFilters().observe(getViewLifecycleOwner(), set -> {
            // Update the selected filters UI
        });
    }
    

    }

    注意这一点

            viewModel = new ViewModelProvider(requireActivity()).get(ListViewModel.class);
    

    requireActivity()确保所有片段都调用宿主活动的上下文。

    您不能以这种方式与活动共享数据,因为视图模型实例在活动被销毁时被销毁

    【讨论】:

      【解决方案4】:

      在我个人看来,您的方法对于这种情况还不错,但是如果想尝试其他方法,我可以建议您使用 RxBus 方法。这是一个很棒的article。使用这种方法,您可以简单地在包含片段的 Activity 中发布数据,然后在所有片段中侦听此特定事件。

      类似:

      //Activity
      RxBus.publish(RxEvent.EventOnBookProvide(bookObject)
      

      //Fragment
      RxBus.listen(RxEvent.EventOnBookProvide::class.java).subscribe {
              useObject(it)
          }
      

      如果使用 Activity,不要忘记在 onDestroy() 中使用 Disposable 和 .dispose(),如果使用 Fragment,请不要忘记使用 onDestroyView()。

      【讨论】:

      • 我认为这对屏幕之间共享数据没有帮助,因为这在内部是一个不保留价值的 PublishSubject。
      • 不,因为没有人保证打开视图可以观察
      • 协程通道优于 RxBus - 因为结构化并发更容易理解。
      猜你喜欢
      • 2011-05-24
      • 1970-01-01
      • 2015-12-06
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2010-11-12
      • 2014-05-06
      • 1970-01-01
      相关资源
      最近更新 更多