bodovix
bodovix

Reputation: 349

Java Android LiveData calls Room query dependent on other LiveData

UPDATE:::

I've updated the question to include demo other LiveData that were also required: so we have userLD that we need the value of to get the goalWeeklyLD, and we need the goalWeeklyLD value to get the remaining 4 LiveData values as they come from Room querys that use goalWeekly.dateproperties in the query

:::::

I've hit a problem where I have a fragment that has to populate LiveData that uses a query dependent on another LiveData value. how can i get my live data to work correctly when it is dependent on other results?

Without using The Transitions.map() the view model throws an error because the values of the other live data are still null. with the Transitions.map() in the view model the activities observer throws an error because the LiveData is still null.

I could possibly cheat my way past this by using a horrendously big nested query to return all i need in one custom DTO. but i'd rather understand whats going on here and how to handle this sort of situation properly.

Hopefully some code will make this clear

The Activity:

public class SomeFragment extends Fragment {

public static SomeFragment newInstance() {
    return new SomeFragment();
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    someViewModel = ViewModelProviders.of(this).get(SomeViewModel.class);


    //getting user details from previous activity
    Intent intent = getActivity().getIntent();
    if (intent != null){
        if (intent.hasExtra(USER_ID)){
            user = new User(intent.getStringExtra(USERNAME));
            user.setId(intent.getLongExtra(USER_ID,0));
            someViewModel.setUserLD(user);
        }
    }


    someViewModel.getUserLD().observe(this, new Observer<User>() {
        @Override
        public void onChanged(@Nullable User userVal) {
            user = userVal;
        }
    });
    someViewModel.getGoalWeeklyLD().observe(this, new Observer<User>() {
        @Override
        public void onChanged(@Nullable User userVal) {
            user = userVal;
        }
    });
    //the below Observer calls throw an error because LiveData is null. makes sense.
    //but how can i say "don't try and observe these until the transition.map has ran (because then it wont be null after if my understanding is right)" or something to that effect
    someViewModel.getFirstLD(user.getId()).observe(this, new Observer<XObject>() {
        @Override
        public void onChanged(@Nullable Grades avgSportGradeVal) {
            //Update UI
        }
    });
    someViewModel.getSecondLD(user.getId()).observe(this, new Observer<XObject>() {
        @Override
        public void onChanged(@Nullable Grades avgBoulderGradeVal) {
            // Update UI
        }
    });
    someViewModel.getThriLD(user.getId()).observe(this, new Observer<XObject>() {
        @Override
        public void onChanged(@Nullable Grades avgBoulderGradeVal) {
            // Update UI
        }
    });
    someViewModel.getFourthLD(user.getId()).observe(this, new Observer<XObject>() {
        @Override
        public void onChanged(@Nullable Grades avgBoulderGradeVal) {
            // Update UI
        }
    });

}}

The View Model:

public class SomeViewModel extends AndroidViewModel {
DaoRepository daoRepository;

MutableLiveData<User> userLD;
LiveData<XObject> firstLD;
LiveData<XObject> secondLD;
LiveData<XObject> thirdLD;
LiveData<XObject> fourthLD;



public MutableLiveData<User> getUserLD() {
    return userLD;
}
public void setUserLD(User user){
    userLD.setValue(user);
}

public LiveData<XObject> getFirstLD(long userId) {
    return goalWeeklyLD;
}
public LiveData<XObject> getSecondLD(long userId) {
    return goalWeeklyLD;
}
public LiveData<XObject> getThirdLD(long userId) {
    return goalWeeklyLD;
}
public LiveData<XObject> getForthLD(long userId) {
    return goalWeeklyLD;
}


public SomeViewModel(@NonNull Application application) {
    super(application);
    daoRepository = new DaoRepository(application);
    userLD = new MutableLiveData<>();


    //so the first LiveData waits for the user to be populated before getting its LiveData becasue we need the userId for our Room query to run
    firstLD = Transformations.map(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()).getValue());

    //the remaining live data uses values from the first...
    setupOtherTransformMaps(userLD.getValue())
}

public void setupOtherTransformMaps(long userId) {
    //the secondLD, thirdLD and fourthLD all depends on values from the first (in runs a query that uses its dateExpired)
    secondLD = Transformations.map(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
    thirdLD = Transformations.map(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
    fourthLD = Transformations.map(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()).getValue());
}}

Upvotes: 2

Views: 758

Answers (1)

EpicPandaForce
EpicPandaForce

Reputation: 81539

Thankfully Google was smart and created a component which lets you combine variable number of LiveData into a single LiveData, and only emit events when you choose to do so!

This is called MediatorLiveData.

In your case though, you only need to channel 1 LiveData (userLD) into 1 another LiveData, that will emit each time userLd has a new value.

So you can use a predefined MediatorLiveData that does exactly this, specifically Transformations.switchMap.

firstLD = Transformations.switchMap(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()));


EDIT: Yup, you seem to need to expose these LiveData separately from one another, but they all depend on the first query to execute.

So you need to replace Transformations.map { ...getValue() with Transformations.switchMap and you'll be good to go.

public SomeViewModel(@NonNull Application application) {
    super(application);
    CustomApplication app = (CustomApplication) application;
    daoRepository = app.daoRepository();
    userLD = new MutableLiveData<>();

    firstLD = Transformations.switchMap(userLD, user -> daoRepository.getMostRecentGoalWeekly(user.getId()));

    secondLD = Transformations.switchMap(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));
    thirdLD = Transformations.switchMap(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));
    fourthLD = Transformations.switchMap(firstLD, first ->
            daoRepository.getAvgGradeRouteInPeriod(userId, first.getDateCreated(),first.getDateExpires()));

}

Upvotes: 2

Related Questions