Search code examples
javaandroidsavestateandroid-savedstateviewmodel-savedstate

How to convert ViewModel to utilize state saving?


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.


Solution

  • 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); }
    
    }