5

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.

nobody special
  • 445
  • 4
  • 16
  • remove this call from constructor of viewmodel setConsentRequired(false); – Vishal Oct 24 '18 at 05:53
  • @Vishal Thanks, I understand it is the constructor assignment that is triggering the first observation, but I want to know why this only happens on the first launch after the app is installed on the device, and never for any other app launch. So when the app is installed and launched, the constructor assignment triggers the observer, then I shut the app down and when I run the app again, the constructor assignment does not trigger the observer. This behaviour is weird. – nobody special Oct 24 '18 at 11:50
  • because ViewModelProviders returns singleton object of your ViewModel , which means when you launch your app ViewModelProviders creates an object and whenever you access that object during same LifeCycle of an application it returns same object. – Vishal Oct 24 '18 at 12:05
  • 1
    @Vishal You are missing the point. I close the app, so it is not the same life cycle. Let me put it this way. I install the app and when I open it, the observer is triggered immediately. Then I close the app, turn the phone off. When I turn the phone back on and open the app, the observer is not triggered. No lifecycle survives cold booting a device. The observer is only triggered when I run the app for the first time after installing it. – nobody special Oct 24 '18 at 19:46

1 Answers1

0

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.

Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
  • I read that myself last night so thanks for confirming my new understanding. But I still don't understand why the constructor assignment is only being triggered the first time after the app is installed on the device. The constructor assignment never triggers the observer for any subsequent app launches. – nobody special Oct 24 '18 at 11:47
  • @nobodyspecial can you clarify, how I can to reproduce _"never triggers the observer for any subsequent app launches"_ step by step – Sergei Bubenshchikov Oct 24 '18 at 11:52
  • (1) Install app and open app, the observer is triggered straight away, then close the app. (2) Open the app after it was closed, the observer is not triggered. Repeatedly closing and re-opening the app never triggers the observer. (3) Delete the app's storage, simulating a fresh installation, open the app and the observer is triggered straight away. – nobody special Oct 24 '18 at 19:43
  • @nobodyspecial I've supplement the answer. It should help you – Sergei Bubenshchikov Oct 25 '18 at 04:36
  • No I do not mean minimize, I mean close. This is happening if I turn the phone off, which is why I don't understand it. I have now refactored my code to not use observers for my required functionality, I can't waste any more time trying to hack around this ViewModel/LiveData problem. I will try and come back to this in a test program at a later date to understand what is going on. – nobody special Oct 25 '18 at 13:23
  • Turned out there was a bug in a library that a colleague wrote which caused the LiveData to be unexpectedly updated, which only became identifiable as I refactored the code. There are times I hate libraries! – nobody special Oct 26 '18 at 19:41
  • @XIII-th I have a similar use case here: https://stackoverflow.com/questions/57650511/android-recyclerviews-cardview-not-showing-every-time I would appreciate if you could offer any insights on how to solve... – AJW Aug 26 '19 at 15:54