Blog Infos
Author
Published
Topics
, , ,
Published
Who’s Next?!

Who’s Next!?

Finite State Machine (FSM)
sealed class State {
object Idle : State()
object SettingTimer : State()
object CountingDown : State()
object Paused : State()
object Restarting : State()
}
view raw State.kt hosted with ❤ by GitHub

FSM states

 

sealed class Event {
object OnSetTimer : Event()
class OnTimerSet(val value: Int) : Event()
object OnStart : Event()
object OnFinish : Event()
object OnPause : Event()
object OnStop : Event()
object OnReset : Event()
}
view raw Events.kt hosted with ❤ by GitHub

FSM events

 

Who’s Next FSM

Finally, our state machine can also produce SideEffects when transitioning between states:

sealed class SideEffect {
object SetTimer : SideEffect()
class TimerReady(val value: Int) : SideEffect()
object StartCountDown : SideEffect()
object Restarting : SideEffect()
object Pause : SideEffect()
object Stop : SideEffect()
object Reset : SideEffect()
}
view raw SideEffect.kt hosted with ❤ by GitHub

FSM side effects

 

Implementation
val stateMachine = StateMachine.create<State, Event, SideEffect> {
initialState(State)
state<State> {
on<Event> {
transitionTo(State, SideEffect)
}
//more events
}
//more states
onTransition {
//process side effects
}
}
view raw FSM.kt hosted with ❤ by GitHub

DSL

 

Let’s recall our diagram and write the FSM code to alternate between both Idle and SettingTimer states:

FROM -> TO by [EVENT]
Idle -> SettingTimer by [OnSetTimer]
SettingTimer -> Idle by [OnTimerSet]
StateMachine.create<TimerState.State, TimerState.Event, TimerState.SideEffect> {
initialState(TimerState.State.Idle)
state<TimerState.State.Idle> {
on<TimerState.Event.OnSetTimer> {
transitionTo(TimerState.State.SettingTimer, TimerState.SideEffect.SetTimer)
}
}
state<TimerState.State.SettingTimer> {
on<TimerState.Event.OnTimerSet> {
transitionTo(TimerState.State.Idle, TimerState.SideEffect.TimerReady(it.value))
}
}
onTransition {
val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition
//emit ui state changes
}
}
view raw FSM.kt hosted with ❤ by GitHub

Idle and SettingTimer configuration

 

And the result is:

FSM operations output

 

Job Offers

Job Offers


    Android Engineer

    American Express
    New York
    • Full Time
    apply now

    ANDROID DEVELOPER (M/F/D)

    Payback GmbH
    Munich, Germany
    • Full Time
    apply now

    Android AOSP Platform Developer (m/w/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

, ,

Rock-Solid UI and Instrumentation Testing

We Android developers use the “flakiness” word more than the “stability” word while talking about Espresso and Instrumentation tests. We cannot truly trust flaky tests. Instrumentation testing will continue to be part of our lives…
Watch Video

Rock-Solid UI and Instrumentation Testing

Sinan Kozak
Android Staff Engineer
Delivery Hero

Rock-Solid UI and Instrumentation Testing

Sinan Kozak
Android Staff Engine ...
Delivery Hero

Rock-Solid UI and Instrumentation Testing

Sinan Kozak
Android Staff Engineer
Delivery Hero

Jobs

Model-View-Intent (MVI)
Implementation
data class TimerUiState(
val value: Int = 0, //in seconds
val progress: Float = 0f, //0-100
val isSettingTimer: Boolean = false,
val isCountingDown: Boolean = false,
val isRestarting: Boolean = false
)
view raw TimerUiState.kt hosted with ❤ by GitHub

MVI State

 

class TimerViewModel : ViewModel() {
private val _state = MutableLiveData<TimerUiState>(TimerUiState())
val state: LiveData<TimerUiState> = _state
fun settingTime() {
_state.value = state.value.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false)
}
fun setTime(seconds: Int) {
_state.value = TimerUiState(seconds)
}
}
//Observing state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.state.observeAsState().value) { /*...*/ }
}

MVVM implementation

 

With Orbit Multiplatform we change it to:

class TimerViewModel : ViewModel(), ContainerHost<TimerUiState, Nothing> {
override val container = viewModelScope.container<TimerUiState, Nothing>(TimerUiState())
fun settingTime() {
intent { reduce { state.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false) } }
}
fun setTime(seconds: Int) {
intent { reduce { TimerUiState(seconds) } }
}
}
//Collecting state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.collectAsState().value) { /*...*/ }
}

MVI implementation

 

class TimerViewModel : ViewModel(), ContainerHost<TimerUiState, Nothing> {
override val container = viewModelScope.container<TimerUiState, Nothing>(TimerUiState())
private val stateMachine = StateMachine.create<TimerState.State, TimerState.Event, TimerState.SideEffect> {
initialState(TimerState.State.Idle)
state<TimerState.State.Idle> {
on<TimerState.Event.OnSetTimer> {
transitionTo(TimerState.State.SettingTimer, TimerState.SideEffect.SetTimer)
}
}
state<TimerState.State.SettingTimer> {
on<TimerState.Event.OnTimerSet> {
transitionTo(TimerState.State.Idle, TimerState.SideEffect.TimerReady(it.value))
}
}
onTransition {
val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition
when (validTransition.sideEffect as TimerState.SideEffect) {
is TimerState.SideEffect.SetTimer -> intent { reduce { state.copy(isSettingTimer = true, isCountingDown = false, isRestarting = false) } }
is TimerState.SideEffect.TimerReady -> intent { reduce { TimerUiState(effect.value) } }
}
}
}
fun settingTime() {
stateMachine.transition(TimerState.Event.OnSetTimer)
}
fun setTime(seconds: Int) {
stateMachine.transition(TimerState.Event.OnTimerSet(seconds))
}
}
//Collecting state changes in the View
@Composable
fun TimerScreen(viewModel: TimerViewModel) {
with(viewModel.collectAsState().value) { /*...*/ }
}

FSM (state: Idle and SettingTimer) + MVI implementation

 

FSM+MVI

Conclusions

Who’s Next!?

Final thoughts

Thanks to Matthew Dolan.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
In this part of the series, we will plan our first screen in Jetpack…
READ MORE
blog
We’ll be selecting a time whenever a user presses a number key. Following points…
READ MORE
blog
Ask yourself a fairly standard question for any interview to fill an Android position:…
READ MORE
blog
This is part of a multi-part series about learning to use Jetpack Compose through…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu