Elye
Elye

Reputation: 60161

How to save LiveData into saveStateHandle?

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?

Upvotes: 8

Views: 11435

Answers (4)

Vahe Gharibyan
Vahe Gharibyan

Reputation: 5683

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

Upvotes: 11

EpicPandaForce
EpicPandaForce

Reputation: 81549

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.

Upvotes: 2

Elye
Elye

Reputation: 60161

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.

Upvotes: 5

Elye
Elye

Reputation: 60161

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.

Upvotes: -2

Related Questions