在ViewModel之间共享数据

时间:2019-02-08 08:06:18

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

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

问题在于,所有ViewModel都需要同一个对象实例(我们将其称为Book)来进行Room事务。

它是否具有在ViewModel之间共享数据(或LiveData)的正确方法?我知道Shared ViewModel的概念,但是我不知道是否可以将其应用于这种情况。我还考虑过使用MediatorLiveData,但也没有很好的方法。

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

这是一个好方法吗?

4 个答案:

答案 0 :(得分:0)

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

类似:

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

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

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

答案 1 :(得分:0)

您应该在片段/活动之间共享这些数据(也许使用Intents进行活动),然后由另一个ViewModel处理这些数据

答案 2 :(得分: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可以完成这项工作。

关于ViewModel:

理论上,每个片段都有自己的Viewmodel。而且,如果您通过使用requireActivity()作为参考来获取它,则可以随处获取每个ViewModel并因此进行共享。

例如:

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

您可以在每个Fragment中调用并获取相同的ViewModel实例。如果您觉得DataRepository设置过大,请制作一个具有Room访问权限的ViewModel并从每个Fragment加载它。

答案 3 :(得分:0)

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

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

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


static JSONObject addKeyToJson_stringValue(@NonNull JSONObject jsonObject, @NonNull String key, @NonNull String value) {
        try {
            jsonObject.put(key, value);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

===

@Test
    public void test_addKeyToJson_stringValue() throws Exception {
        JSONObject jsonObjectSpy = Mockito.spy(new JSONObject());
        Mockito.when(jsonObjectSpy.put(anyString(), any())).thenThrow(new JSONException("!!! fake exception"));
        Util.addKeyToJson_stringValue(jsonObjectSpy, "key", "value");
        assertEquals(0, jsonObjectSpy.length());
    }

}

注意这一点

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); 确保所有片段都调用宿主活动的上下文。

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