I have developed a KMP project that successfully builds in iOS and Android. I noticed that I am unable to background the Android app and resume the activity. It will always recreate the activity and lose all user inputs done previously.
It appears that the main issue arises from a write exception to a serializable object. I am just not sure where to go from here and how much code I should share here (or link to whole repository) for people who have experienced a similar problem to reproduce this problem. Any advice gratefully received.
Android Studio Giraffe | 2022.3.1 Patch 2 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. macOS 13.6.1
Kotlin Multiplatform version 1.9.10, Gradle Plugin 8.1.0
FATAL EXCEPTION: main
Process: org.haspohr.cmr, PID: 3145
android.os.BadParcelableException: Parcelable encountered IOException writing serializable object (name = org.haspohr.cmr12.screens.StartScreen)
at android.os.Parcel.writeSerializable(Parcel.java:2797)
at android.os.Parcel.writeValue(Parcel.java:2563)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeList(Parcel.java:1415)
at android.os.Parcel.writeValue(Parcel.java:2506)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeList(Parcel.java:1415)
at android.os.Parcel.writeValue(Parcel.java:2506)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1298)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1843)
at android.os.Bundle.writeToParcel(Bundle.java:1389)
at android.os.Parcel.writeBundle(Parcel.java:1367)
at android.os.Parcel.writeValue(Parcel.java:2479)
at android.os.Parcel.writeValue(Parcel.java:2369)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1298)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1843)
at android.os.Bundle.writeToParcel(Bundle.java:1389)
at android.os.Parcel.writeBundle(Parcel.java:1367)
at android.os.Parcel.writeValue(Parcel.java:2479)
at android.os.Parcel.writeValue(Parcel.java:2369)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1917)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1954)
at android.app.servertransaction.PendingTransactionActions$StopInfo.collectBundleStates(PendingTransactionActions.java:123)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:139)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.io.NotSerializableException: org.haspohr.cmr12.GameViewModel
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1620)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1581)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1490)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:2792)
at android.os.Parcel.writeValue(Parcel.java:2563)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeList(Parcel.java:1415)
at android.os.Parcel.writeValue(Parcel.java:2506)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeList(Parcel.java:1415)
at android.os.Parcel.writeValue(Parcel.java:2506)
at android.os.Parcel.writeValue(Parcel.java:2362)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1298)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1843)
at android.os.Bundle.writeToParcel(Bundle.java:1389)
at android.os.Parcel.writeBundle(Parcel.java:1367)
at android.os.Parcel.writeValue(Parcel.java:2479)
at android.os.Parcel.writeValue(Parcel.java:2369)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1298)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1843)
at android.os.Bundle.writeToParcel(Bundle.java:1389)
at android.os.Parcel.writeBundle(Parcel.java:1367)
at android.os.Parcel.writeValue(Parcel.java:2479)
at android.os.Parcel.writeValue(Parcel.java:2369)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1917)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1954)
at android.app.servertransaction.PendingTransactionActions$StopInfo.collectBundleStates(PendingTransactionActions.java:123)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:139)
at android.os.Handler.handleCallback(Handler.java:958)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
When I minimise the app by pressing escape in the simulator or going to home background on a real device, it should be able to resume the activity. Instead, the app will always restart, losing previous inputs.
Here is the beginning of the GameViewModel class:
class GameViewModel{
private val _uiState = MutableStateFlow(GameUiState())
val uiState: StateFlow<GameUiState> = _uiState.asStateFlow()
var focussed:Int=0
init {
updateBSA()
updateECV()
}
fun reset()
{
_uiState.value=GameUiState()
updateBSA()
updateECV()
accept()
focussed=0
}
fun updateWeight(w: String) {
_uiState.update { currentState -> currentState.copy(weightVal = if (w.isNotBlank()) w else "") }
_uiState.update { currentState -> currentState.copy(bsaChanged = true) }
updateBSA()
}
fun togglepapillary(i:Int) {
_uiState.update { currentState -> currentState.copy(papillary = i==1)}
}
When I change the class declaration to:
class GameViewModel: Serializable {
Serializable is underlined red, with advice: This type has a constructor, and thus must be initialized here. This type is final, so it cannot be inherited from.
Thank you for your suggestions. Eventually, it turns out the main problem was related to the handling of the ViewModel by the navigation library (Adriel Cafe Voyager). I avoided the problem by defining the ViewModel as a companion object in my MainApp class, and then accessing it when necessary directly, rather than passing it to the Navigator class.