Reputation: 980
I have a simple Text
Element where I want to display the current time.
I tried to start a periodically Timer Task
but it says APP STOPPED WORKING
. What is the correct approach to periodically update my var currentTime by mutableStateOf("12:00")
inside another Thread so it forces a correct recomposition without crashing?.
class MyViewModel : ViewModel() {
var currentTime by mutableStateOf("12:00")
init {
val clockTimer = Timer()
clockTimer.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
val hoursAndMinutes = SimpleDateFormat("HH:mm", Locale.US).format(Date())
Log.d("DEBUG", "Current Time is: $hoursAndMinutes")
currentTime = hoursAndMinutes
}
}, 0, 60000)
}
}
@Preview
@Composable
fun ClockView(myViewModel: MyViewModel = viewModel()){
Text(
text = myViewModel.currentTime
)
}
FATAL EXCEPTION: Timer-0
Process: com.myapp.c, PID: 15437
java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied
at androidx.compose.runtime.snapshots.SnapshotKt.readError(Snapshot.kt:1857)
at androidx.compose.runtime.snapshots.SnapshotKt.current(Snapshot.kt:2110)
at androidx.compose.runtime.SnapshotMutableStateImpl.setValue(SnapshotState.kt:299)
at com.myapp.c.ui.MyViewModel.setCurrentTime(Test.kt:41)
at com.myapp.c.ui.MyViewModel$1.run(Test.kt:25)
at java.util.TimerThread.mainLoop(Timer.java:562)
at java.util.TimerThread.run(Timer.java:512)
Upvotes: 1
Views: 2286
Reputation: 306
The Snapshots are transactional, hence needed to be run on main thread here,
And cannot be updated from another thread except main
.
Hence to update/read the value you need to be on UI thread, i.e. main thread.
run this currentTime = hoursAndMinutes
on main thread, by wrapping it in coroutine, like
viewModelScope.launch {
currentTime = hoursAndMinutes
}
As viewModelScope
is bound to Dispatchers.Main.immediate
, it will run on main thread.
Upvotes: 2