0

I have a ViewModel I am currently using to contain data and share values between Fragments. This model also helps instantiate the data for the app on activity start.

I am now trying to add in state saving functionality to my app, and I'm confused on how to merge the two systems together. The android docs mention to use this constructor:

public MyViewModel(SavedStateHandle savedStateHandle) {
    mState = savedStateHandle;
}

However, I'm unsure on how the state is passed and how this constructor is used in activities (here is my usage):

 myViewModel = new ViewModelProvider(requireActivity()).get(myViewModel.class);

Anyway, since I need to also instantiate data in case the savedState is null, I'm not sure how that part fits in. For reference, here is a general outline of my ViewModel class:

public class myViewModel extends ViewModel {
//    private MutableLiveData<Integer> foo;  <-- obsolete with state saving

    private SavedStateHandle mState;
    private static final String FOO_KEY = "foo";

    // Do I need this anymore? How do I combine this with the other constructor?
    public myViewModel() {
        foo = new MutableLiveData<>();
        foo.setValue(4);
    }

    // Constructor for the savedStateHandle
    public myViewModel(SavedStateHandle savedStateHandle) { mState = savedStateHandle; }

    LiveData<Integer> getFoo() { return mState.getLiveData(FOO_KEY); }

    void setFoo(int foo) { mState.set(FOO_KEY, foo); }

}

Obviously if I take out the old constructor and MutableLiveData member, then when I access the ViewModel in my fragments, the data will be null (because I haven't told the activity to explicitly save a state yet), and I haven't instantiated any data.

Matt Strom
  • 698
  • 1
  • 4
  • 23

1 Answers1

2

You don't need your no argument constructor. Instead, you should use the other getLiveData() method that takes an initial value:

public class myViewModel extends ViewModel {

    private SavedStateHandle mState;
    private static final String FOO_KEY = "foo";

    public myViewModel(SavedStateHandle savedStateHandle) {
        mState = savedStateHandle;
    }

    LiveData<Integer> getFoo() {
        // Get the LiveData, setting the default value if it doesn't
        // already have a value set.
        return mState.getLiveData(FOO_KEY, 4);
    }

    void setFoo(int foo) { mState.set(FOO_KEY, foo); }

}
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • This looks promising, I'll give it a shot today – Matt Strom Jun 04 '20 at 13:02
  • Thanks for the great answer, this is just what I needed. I combined this with a logic check to make sure the key is filled or not: `if (!savedState.contains(MY_KEY) { return getLiveData(MY_KEY, defaultValue) }` – Matt Strom Jun 04 '20 at 17:59
  • @MatthewStrom - you don't need to do that - `getLiveData` does that for you, only applying the default value if there isn't a value already set. – ianhanniballake Jun 04 '20 at 18:01