在ViewModel之间共享数据

23

我在我的项目中有一个复杂的屏幕,我将它分解成多个片段。我正在尝试为这些类遵循MVVM架构,因此每个片段都有自己的ViewModel和Contract类。

问题是所有的ViewModel都需要同一个对象实例(我们称之为Book)来进行数据库(Room)事务。

有没有一种正确的方式在ViewModel间共享数据(或LiveData)?我知道Shared ViewModel的概念,但不知道是否适用于这种情况。我也考虑使用MediatorLiveData,但也没有好的方法。

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

这是否是一个好的方法?


每个活动使用单个ViewModel,这样您的片段将自动共享相同的ViewModel。 - Roshaan Farrukh
@RoshaanFarrukh 分离类的目的是为了不让ViewModel过于臃肿,以免将来会有更多的方法。 - Igor Escodro
你找到好的方法了吗?目前我正在一个SharedViewModel中通过repository获取数据,然后在我的activity中观察这些数据。在observe函数调用中,我还将其更新到其他ViewModel实例中,这些实例我也在所有的fragment中访问。 - nulldroid
@IgorEscodro 我想我在这里有同样的问题:https://stackoverflow.com/q/60796010/8258130 你解决了吗?请分享。 - iadcialim24
@nulldroid 是的,那也可以行!如果你有空的话,能否看一下我的帖子,链接在这里 https://stackoverflow.com/q/60796010/8258130 - iadcialim24
4个回答

0

我也遇到了同样的问题。但是如果你没有为各个片段使用不同的视图模型,或者设计不需要使用不同的视图模型,那么你可以在整个活动(所有其他片段)之间共享一个片段,并且它们将共享相同的数据实例。

点击此链接获取更多信息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() 确保所有片段调用宿主活动的上下文。

但是,您无法通过这种方式与活动共享数据,因为当活动被销毁时,视图模型实例也会被销毁。


0

你应该在片段/活动之间共享这些数据(也许可以使用活动的意图),然后由另一个ViewModel处理这些数据


是的,那是一种方法。但问题在于让视图处理数据并保持所有数据同步的方式。 - Igor Escodro

0
答案和往常一样,取决于具体情况。 如果你提问的原因是房间访问,那么建议有一个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就可以胜任。

关于ViewModels:

理论上,每个片段都有自己的ViewModel。如果您使用requireActivity()作为引用来获取它,您可以随时获取每个ViewModel并因此共享它。

例如:

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

你可以在每个Fragment中调用此方法并获取相同的ViewModel实例。如果DataRepository的设置对你来说过于繁琐,可以创建一个带有Room访问权限的ViewModel,并从每个Fragment中加载它。


-1
在我个人看法中,你的方法对于这种情况并不差,但如果想尝试其他方法,我可以建议你使用 RxBus 方法。这里有一篇很棒的 文章。通过这种方法,你可以简单地在包含片段的活动中发布数据,然后在所有片段中侦听此特定事件。
类似这样:
//Activity
RxBus.publish(RxEvent.EventOnBookProvide(bookObject)

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

而且不要忘记在 onDestroy()(如果使用 Activity)和 onDestroyView()(如果使用 fragment)中使用 Disposable 和 .dispose()。


1
我认为这并不有助于在屏幕之间共享数据,因为这是一个内部不保留值的PublishSubject。 - EpicPandaForce
1
不行,因为没有人保证视图是开放的以供观察。 - Stavro Xhardha
1
协程通道优于RxBus - 因为结构化并发更易于理解。 - IgorGanapolsky
@IgorGanapolsky 那么多个 ViewModel 之间如何共享数据? - Sumit Shukla
@SumitShukla 多个ViewModel可以订阅同一个“Channel”。现在最好使用“SharedStateFlow”。 - IgorGanapolsky

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