8

As shown in https://stackoverflow.com/a/61166665/3286489, we could save LiveData in savedStateHandle.

I could do that easily https://stackoverflow.com/a/61166665/3286489

private val textLiveData: MutableLiveData<String>

init {
    textLiveData = savedStateHandle.getLiveData(KEY)
}

However, when trying to save it as below,

savedStateHandle.set(KEY, textLiveData)

I got the error

java.lang.IllegalArgumentException: Can't put value with type class androidx.lifecycle.SavedStateHandle$SavingStateLiveData into saved state

Where did I get it wrong?

Elye
  • 53,639
  • 54
  • 212
  • 474
  • 1
    Try that way `savedStateHandle.set(KEY, textLiveData.value)` – Parth Apr 12 '20 at 04:39
  • 1
    @Parth this is bad advice (well, not the best advice). You can use `val textLiveData: MutableLiveData = savedStateHandle.getLiveData(KEY)` which constructs a `MutableLiveData` that is automatically persisted to `savedInstanceState`. – EpicPandaForce Apr 17 '20 at 20:39

4 Answers4

11

That is because your you class is not acceptable. You can put Parcelable or Serializable types.

Here is the list of ACCEPTABLE_CLASSES

private static final Class[] ACCEPTABLE_CLASSES = new Class[]{
        //baseBundle
        boolean.class,
        boolean[].class,
        double.class,
        double[].class,
        int.class,
        int[].class,
        long.class,
        long[].class,
        String.class,
        String[].class,
        //bundle
        Binder.class,
        Bundle.class,
        byte.class,
        byte[].class,
        char.class,
        char[].class,
        CharSequence.class,
        CharSequence[].class,
        // type erasure ¯\_(ツ)_/¯, we won't eagerly check elements contents
        ArrayList.class,
        float.class,
        float[].class,
        Parcelable.class,
        Parcelable[].class,
        Serializable.class,
        short.class,
        short[].class,
        SparseArray.class,
        (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Size.class : int.class),
        (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? SizeF.class : int.class),
};
Vahe Gharibyan
  • 5,277
  • 4
  • 36
  • 47
5

Apparently the better answer is, we don't even need to

savedStateHandle.set(KEY, textLiveData.value)

While that is permissible, when we one do set

textLiveData.value = "Some data"

This already saved to the state. As textLiveData is retrieved from savedStateHandle as below.

textLiveData = savedStateHandle.getLiveData(KEY) 

Hence the textLiveData is already stored within the savedStateHandle, and changes to textLiveData.value is inadvertently get saved in savedStateHandle.

Elye
  • 53,639
  • 54
  • 212
  • 474
2

You can use val textLiveData: MutableLiveData<String> = savedStateHandle.getLiveData(KEY) which constructs a MutableLiveData that is automatically persisted to savedInstanceState.

savedStateHandle.set(KEY, textLiveData)

You don't need to do this, because getLiveData is a SavingStateLiveData which automatically handles this.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
-1

After looking into the code, realize I just need to save the value, as what @Parth mentioned in the comment above.

savedStateHandle.set(KEY, textLiveData.value)

Explanation

If we look into the set of SavedStateHandle.java

@MainThread
public <T> void set(@NonNull String key, @Nullable T value) {
    validateValue(value);
    @SuppressWarnings("unchecked")
    MutableLiveData<T> mutableLiveData = (MutableLiveData<T>) mLiveDatas.get(key);
    if (mutableLiveData != null) {
        // it will set value;
        mutableLiveData.setValue(value);
    } else {
        mRegular.put(key, value);
    }
}

This shows that it will put the data into the mutableLiveData that we store in mLiveDatas if there is one.

To ensure that our liveData is in mLiveDatas, we just need to use textLiveData = savedStateHandle.getLiveData(KEY) at the start. Checkout the getLiveData of SavedStateHandle.java

@NonNull
private <T> MutableLiveData<T> getLiveDataInternal(
        @NonNull String key,
        boolean hasInitialValue,
        @Nullable T initialValue) {
    MutableLiveData<T> liveData = (MutableLiveData<T>) mLiveDatas.get(key);
    if (liveData != null) {
        return liveData;
    }
    SavingStateLiveData<T> mutableLd;
    // double hashing but null is valid value
    if (mRegular.containsKey(key)) {
        mutableLd = new SavingStateLiveData<>(this, key, (T) mRegular.get(key));
    } else if (hasInitialValue) {
        mutableLd = new SavingStateLiveData<>(this, key, initialValue);
    } else {
        mutableLd = new SavingStateLiveData<>(this, key);
    }
    mLiveDatas.put(key, mutableLd);
    return mutableLd;
}

It will create one and put into mLiveDatas when we request for one, if there isn't one already.

Elye
  • 53,639
  • 54
  • 212
  • 474