Skip to content

Repository files navigation

Compose Runtime as a Logic Engine (State Machine)

🇷🇺 Русский | 🇺🇸 English

An experimental Android project demonstrating the use of Jetpack Compose Runtime outside the graphical user interface (GUI) to manage application business logic.

📖 Concept Traditionally, Jetpack Compose is perceived solely as a tool for building UI. However, as described in the seminal article A Jetpack Compose by Any Other Name by Jake Wharton, Compose consists of two independent parts:

  1. Compose UI: A set of visual components (Text, Column, Button, etc.).
  2. Compose Runtime: A powerful declarative engine for managing node trees, tracking state changes, and handling side effects (Effects).

This project leverages the Runtime as a "state prediction engine," transforming imperative logic into a declarative state machine.


🎨 Approach Comparison

1. Traditional Approach (ViewModel + Flow)

In classic architecture, merging multiple data sources and handling events often turns into a complex chain of reactive operators:

// Traditional way (risk of "operator spaghetti")val state = combine(flowA, flowB, eventFlow) { a, b, event ->when(event) {
isLogin-> performLogin(a, b)
else->Initial
}
}.stateIn(scope, SharingStarted.Lazily, Initial) 

2. Compose Runtime Approach

Logic is described declaratively. Instead of flatMapLatest, combine, or zip, you use standard Kotlin control flow (if, when) and state variab

// The approach used in this project
@Composable
funAuthStateMachine(event:AuthEvent, repo:IAuthRepository) {
var state by remember { mutableStateOf<AuthState>(...) }
// Logic looks like standard sequential code rather than a callback chainLaunchedEffect(event) {
// Old coroutines are automatically cancelled when a new event arriveswhen (event) {
isAuthEvent.Login-> {
repo.login(...)
state =AuthState.LoggedIn
}
AuthEvent.Logout-> {
repo.logout(...)
state =AuthState.LoggedOut
}
}
}
}

Core idea: We write logic as simply as UI. Instead of reactive operators, we use standard if/when blocks and LaunchedEffect. In this specific implementation, a traditional ViewModel becomes optional.

🛡 Structured Concurrency "Out of the Box"

In a traditional ViewModel, re-triggering an event often requires manual Job management (canceling the previous request). In this approach, LaunchedEffect(event) automatically cancels the previous coroutine as soon as a new event is received, preventing Race Conditions without any extra boilerplate.

🛠 Technical Implementation

The system consists of two key layers:

1. AuthInteractor: Acts as the host for the Compose environment. It initializes the Recomposer, provides the necessary FrameClock, and manages the Composition lifecycle.
2. AuthStateMachine: A Composable function that encapsulates the business logic and is responsible for generating the UI state.

🔄 Workflow

1. The UI calls interactor.send(Event).
2. A Recomposition is triggered within the Interactor.
3. LaunchedEffect reacts to the new Event and executes the required logic (e.g., a network request).
4. The result is stored in a mutableStateOf snapshot.
5. The updated state is emitted via callback to a StateFlow, which the UI observes for rendering

About

Declarative State Machine for Android using Jetpack Compose Runtime outside of UI

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages