How to save LiveData into saveStateHandle?
Asked Answered
A

4

8

As shown in https://mcmap.net/q/1324350/-viewmodel-savedstatehandler-get-for-livedata-can-39-t-set-default-value-as-per-the-document, we could save LiveData in savedStateHandle.

I could do that easily https://mcmap.net/q/1324350/-viewmodel-savedstatehandler-get-for-livedata-can-39-t-set-default-value-as-per-the-document

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?

Adelric answered 12/4, 2020 at 4:16 Comment(2)
Try that way savedStateHandle.set(KEY, textLiveData.value)Disown
@Disown this is bad advice (well, not the best advice). You can use val textLiveData: MutableLiveData<String> = savedStateHandle.getLiveData(KEY) which constructs a MutableLiveData that is automatically persisted to savedInstanceState.Bulldog
A
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.

Adelric answered 12/4, 2020 at 13:18 Comment(2)
Refer to medium.com/@elye.project/… for more details of the finding.Adelric
so what is the solution?how to remove the exception?Idio
R
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),
};
Rodriguez answered 23/11, 2021 at 17:20 Comment(0)
A
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.

Adelric answered 12/4, 2020 at 13:18 Comment(2)
Refer to medium.com/@elye.project/… for more details of the finding.Adelric
so what is the solution?how to remove the exception?Idio
B
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.

Bulldog answered 17/4, 2020 at 20:39 Comment(0)
A
-2

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.

Adelric answered 12/4, 2020 at 4:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.