I have an android watch app that i am building. Disclaimer, I'm a Java engineer with some early experience in android, say 10 years ago so I'm not overly familiar with Kotlin or Jetpack Compose.
I've written an android wear app to keep track of scores in a game as a referee and I'm running into problems persisting data when the app is dismissed. As it stands I've implemented a ViewModel and it's correctly wired into the UI to keep track of the scores / state as the app is open. However, because the swipe to dismiss gesture kills the app, if this happens accidentally in a game it can be disastrous as the tallied score is lost. I'm unable to get android to prevent the swipe gesture.
I've tried umpteen online tutorials but I can't find anything that works. Essentially, In my head I reason about this as: I want to wire into the onDestroy callback to persist data to the device, In onResume I want to read the data from the device and put it into the viewModel so the app can remember it's state. If anyone has used a stopwatch app on a wear device, the state is persisted regardless of dismissal or not. How can I do this in my app? The codebase (Small that it is) is located https://github.com/mcgowanb/refscorekeeper.
The idea is to always persist any new data as soon as it is entered, in a DataStore or a database like Room. For a stop watch it would suffice to save the time the timer was started. Then if the app is closed for any reason (user swiped it away, it crashed or was killed by the Android system, ...) no data needs to be saved, all relevant data is already saved. That way you can restore everything the next time the app is started.
A DataStore is especially easy to use for this since you can use it to store key/value pairs and use it as the Single Source of Truth for this data - you would not just read the data from the DataStore when your app was closed unexpectedly, even when the app is running and the user enters a piece of data, that data is saved to the DataStore first and then retrieved to be displayed in the UI. That sounds like a lot of overhead, but the DataStore caches all data it saves so you can instantly retrieve it without the penalty of accessing the filesystem. Moreover, the content of the DataStore is retrieved as a Flow. That way you automatically get updates if anything changes. And if you use Compose for your UI, the UI is also updated automatically, without any callbacks involved.
When you have set up your DataStore, just call stateIn()
on the provided flow and save the resulting StateFlow
in a property in your ViewModel:
val currentScore = dataStore.currentScore().stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = /* default value until the flow is started. */,
)
Compose can then read the data from the flow as a State object that can be used to automatically update your UI:
val currentScore by viewModel.currentScore.collectAsStateWithLifecycle()
(needs dependency androidx.lifecycle:lifecycle-runtime-compose
in gradle)
Now, when a new value is entered, just save it in the DataStore and the UI is automatically updated accordingly. If your app is closed and restarted, the latest score is automatically read from the DataStore and displayed in the UI.