Weird AndroidViewModel LiveData observer behaviour

Viewed 1087

I say weird because I don't understand what someone is probably going to tell me is working as intended.

I have a AndroidViewModel with LiveData members that I observe in the MainActivity to switch some code functionality. The LiveData objects are assigned initial values in the view model's constructor.

Everything works fine in theory except that the observer behaviour changes between the first time the app is launched after installation, and subsequent app launches.

During the first launch after installation, the observers are triggered instantly after I set them up, without the underlying LiveData objects being changed.

During subsequent launches of the app, the observers are not triggered prematurely after setup, but are only triggered as I change the values elsewhere in the app, which is what I expect to happen.

Originally I thought that the observers were somehow getting a delayed trigger from the LiveData initialization, but if that were true then it should happen regardless of whether it is the first run after installation or subsequent launches.

So in order to get the app to run as intended, I have to use a sentry in the observers to prevent them functioning during the first trigger if the app is being run for the first time after installation.

Can someone explain why this is happening and if it is intended functionality, which I don't believe, point me to the documentation that explains the this?

I feel like I am hacking Android again.

Here's some code snippets as people always ask for them, starting with the LiveData declaration.

@NonNull
private final MutableLiveData<Boolean> consentRequired = new MutableLiveData<>();

ViewModel constructor initialization

    setConsentRequired(false);

ViewModel getter/setter

@NonNull
public LiveData<Boolean> getConsentRequired()
{
    return consentRequired;
}
@NonNull
public void setConsentRequired(@NonNull Boolean consentRequired)
{
    this.consentRequired.setValue(consentRequired);
}

observer

    getViewModel().getConsentRequired().observe(this, item ->
    {
        if (sentryAllowsObserverToRun)
        {
            // Do the observer stuff here
        }
    }

The sentryAllowsObserverToRun is the boolean I have to set to state that this is not the first trigger for the first app launch after installation.

1 Answers

The answer to question is held in this note:

... observers also receive an update when they change from an inactive to an active state. Furthermore, if the observer changes from inactive to active a second time, it only receives an update if the value has changed since the last time it became active.

In your case consentRequired have new data (assigned in vm constructor) and when your MainActivity start to observe data and become active, consentRequired deliver data to MainActivity.
To solve it, you ned to avoid set temporally initial data to LiveData.

I suppose, by "close application" you actually mean "minimize application". In this case, the application process remains alive, and the activity goes to the background state. Until activity remain in backtack, viewmodel also remain in memory. When you "reopen application", activity return to foreground state, but documentation says:

Furthermore, if the observer changes from inactive to active a second time, it only receives an update if the value has changed since the last time it became active.

This lead to: your activity will not receive data when it remain in backstack and LiveData's value remain not changed.

You can read detail description with example in Observe LiveData objects paragraph of documentation.

Related