【问题标题】:How to trigger LiveData SwitchMap even if there is no observer attached即使没有附加观察者,如何触发 LiveData SwitchMap
【发布时间】:2019-07-16 10:27:04
【问题描述】:

问题:

switchMap 转换在其结果必须有一个活动的观察者之前不会触发。即使没有观察者,我也试图触发 switchMap 转换。谁能建议我如何实现此功能?下面有一些描述当前场景的代码。

如何重现:

在 ProfileViewModel 中,UI 可以观察到两个不同的 LiveData

  • profileLiveData:观察个人资料数据
  • profileApiStateLiveData :观察从后端获取配置文件数据的 Http Call 的状态

ProfileViewModel.java

public class ProfileViewModel extends AndroidViewModel {

    private MutableLiveData<Boolean> getProfileCommand = new MutableLiveData<>();

    public ProfileViewModel(@NonNull Application application) {
        super(application);
        profileLiveData = Transformations.switchMap(getProfileCommand, forceUpdate -> UserRepository.getInstance().getProfile(forceUpdate));
        profileApiStateLiveData = UserRepository.getInstance().getProfileApiRequestStatus();
    }

    public void loadProfile(boolean forceUpdate) {
        getProfileCommand.postValue(forceUpdate);
    }

    // Profile Data : Observable Field
    private LiveData<Profile> profileLiveData;
    public LiveData<Profile> getProfileLiveData() {
        if(profileLiveData == null) profileLiveData = new MutableLiveData<>();
        return profileLiveData;
    }

    // Profile Http Call's State : Observable Field
    private LiveData<ApiRequest>  profileApiStateLiveData;
    public LiveData<ApiRequest>  getProfileApiStateLiveData() {
        if(profileApiStateLiveData == null) profileApiStateLiveData = new MutableLiveData<>();
        return profileApiStateLiveData;
    }
}

现在 UI 可以观察到配置文件数据和 Http 调用状态的变化。 所以如果一个 UI 想要下载并在 UI 上显示 Profile,UI 负责观察 profileLiveDataprofileApiStateLiveData 然后调用 ViewModel 的方法 loadProfile(true);

// Observes Profile Data
mProfileViewModel.getProfileLiveData().observe(this, profileData -> {
    // Use profile data here
});

// Observes State of Profile Http Call
mProfileViewModel.getProfileApiStateLiveData().observe(this, profileHttpCallState -> {
    // show/hide progress based on http call state
});

// start downloading profile data
mProfileViewModel.loadProfile(true);

我们可以看到,loadProfile方法会触发switchMap Transformation,并启动Http Call。请注意,switchMap 触发的发生是因为 UI 正在观察结果 LiveData,即 profileLiveData

此方案运行良好。但是如果某个Activity只想发起Http调用,只想观察profileApiStateLiveData而不是profileLiveData,那么switchMap Trigger永远不会发生,这是因为没有结果 LiveData profileLiveData 的积极观察者。

    /**** Do not observe Profile Data, because here we only need to observe Http Call State  ***/

    /**** Only Observe State of Profile Http Call  ***/
    mProfileViewModel.getProfileApiStateLiveData().observe(this, profileHttpCallState -> {
        // show/hide progress based on http call state
    });

    /**** start downloading profile data ***/
    mProfileViewModel.loadProfile(true);

    /**** The above line does not trigger the switchMap Transformation. ***/

我有一个丑陋的解决方案,我必须在 UI 中为 profileLiveData 添加一个不必要的空白观察者。但这很容易出错,因为其他开发人员可能会忘记为 profileLiveData 添加这个不必要的空白观察者,并且即使他们正在调用该方法,他们也不知道为什么没有获取配置文件数据强>loadProfile(true).

非常感谢 Rx 专家的帮助 :-)

【问题讨论】:

  • 不知道为什么要以这种方式设计这两个 LiveData。在获取数据时,为什么不更新profileLiveDataprofileApiStateLiveData。或者,如果您有一个直接使用它发出 LiveData 映射 profileLiveData 的数据库层?
  • 这两个 LiveData 都直接来自房间数据库。 http层(WorkManager)正在分别更新这些表记录。
  • 那么我会让profileLiveData 直接从数据库发出数据,而不是在profileApiStateLiveData 更改时发出数据。当 DB 中的数据发生变化时,profileLiveData 应该发出新数据。 profileApiStateLiveData 应该只在 API 状态发生变化时发出变化。

标签: android rx-android android-architecture-components android-livedata


【解决方案1】:

感谢@arka-prava-basu 已经很好地回答了这个问题。 profileLiveData 已从 Transformation 中分离出来,现在直接从 Room Database 中获取。以及动作

(UserRepository.getInstance().getProfile(forceUpdate))

由这个转换执行的现在已经转移到 loadProfile 方法。下面是重构后的代码。

ProfileViewModel.java

public class ProfileViewModel extends AndroidViewModel {

   public ProfileViewModel(@NonNull Application application) {
        super(application);
        profileLiveData = UserRepository.getInstance().getMemberInfo();
        profileApiStateLiveData = UserRepository.getInstance().getProfileApiRequestStatus();
    }

    public void loadProfile(boolean forceDownload) {
        if(forceDownload) {
            UserRepository.getInstance().initiateProfileDownloading();
        }
    }

    // Profile Data : Observable Field
    private LiveData<Profile> profileLiveData;
    public LiveData<Profile> getProfileLiveData() {
        return profileLiveData;
    }

    // Profile Http Call's State : Observable Field
    private LiveData<ApiRequest>  profileApiStateLiveData;
    public LiveData<ApiRequest>  getProfileApiStateLiveData() {
        return profileApiStateLiveData;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多