在 ViewModel 之间共享数据

Posted

技术标签:

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

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

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

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

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

这是一个好方法吗?

【问题讨论】:

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

以上是关于在 ViewModel 之间共享数据的主要内容,如果未能解决你的问题,请参考以下文章

使用新的架构组件 ViewModel 在片段之间共享数据

Android:单Activity多Fragment,Navigation实现Fragment跳转,Fragment之间通过ViewModel共享数据

Android:单Activity多Fragment,Navigation实现Fragment跳转,Fragment之间通过ViewModel共享数据

在 SwiftUI 组件之间共享 ViewModel

Android 多个Activity(Fragment)之间共享一个ViewModel对象,并扩展作用域注解

如何转换 ViewModel 以利用状态保存?