# How Decompose Handles State Preservation Across Process Death on Android

> Decompose ensures Android state preservation across process death using SavedStateRegistry and kotlinx serialization for automatic data backup and restore.

- Repository: [Arkadii Ivanov/decompose](https://github.com/arkivanov/decompose)
- Tags: how-to-guide
- Published: 2026-02-25

---

**Decompose preserves component state across Android process death by bridging its `StateKeeper` API to the Jetpack `SavedStateRegistry`, automatically serializing data via `kotlinx.serialization` and restoring it when the system recreates your activity.**

Decompose is a Kotlin Multiplatform library for building component-based UIs that treats state preservation as a first-class feature. On Android, the library handles the complexity of surviving process death through a tight integration with AndroidX Saved State. This article examines the internal architecture—specifically how `ComponentContext` exposes state preservation and how `DefaultJetpackComponentContext` wires it to the Android framework.

## The StateKeeper Architecture: From ComponentContext to Android

Decompose abstracts platform-specific state preservation behind the `StateKeeper` API. This design allows components to remain platform-agnostic while the framework handles Android-specific lifecycle events.

### ComponentContext as StateKeeperOwner

Every Decompose component receives a `ComponentContext` in its constructor. This interface extends `StateKeeperOwner`, granting direct access to a `StateKeeper` instance.

In [`decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/decompose/src/commonMain/kotlin/com/arkivanov/decompose/ComponentContext.kt), the interface hierarchy establishes this contract:

```kotlin
interface ComponentContext : StateKeeperOwner, InstanceKeeperOwner, BackHandlerOwner {
    // ComponentContext aggregates essential lifecycle owners
}

```

The `StateKeeperOwner` provides the `stateKeeper: StateKeeper` property, which components use to `consume` previously saved state or `register` new state for preservation.

### The Jetpack Bridge: DefaultJetpackComponentContext

On Android, Decompose creates a `DefaultJetpackComponentContext` that bridges the generic `StateKeeper` to Android's `SavedStateRegistry`. This implementation resides in [`jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/DefaultJetpackComponentContext.kt`](https://github.com/arkivanov/decompose/blob/main/jetpack-component-context/src/commonMain/kotlin/com/arkivanov/decompose/jetpackcomponentcontext/DefaultJetpackComponentContext.kt).

The critical linkage occurs in the `init` block (lines 58-64):

```kotlin
// Restore previously saved state from the Android bundle
stateKeeper.consume(key = KEY, strategy = SavedStateSerializer)
    .also { savedState -> savedStateRegistryController.performRestore(savedState) }

// Save the current state into the Android bundle when required
stateKeeper.register(key = KEY, strategy = SavedStateSerializer) {
    savedState().also(savedStateRegistryController::performSave)
}

```

The `SavedStateSerializer` implements `StateKeeper.Serializer<Bundle>`, converting between Decompose's internal representation and Android `Bundle` objects that the system persists across process death.

## Implementing State Preservation in Components

Developers interact with state preservation through two primary patterns: manual `consume`/`register` calls or the experimental `saveable` property delegate.

### Manual State Management with consume and register

For explicit control, components call `stateKeeper.consume()` during initialization and `stateKeeper.register()` in the `init` block:

```kotlin
import com.arkivanov.decompose.ComponentContext
import kotlinx.serialization.Serializable

class CounterComponent(componentContext: ComponentContext) : ComponentContext by componentContext {

    // Restore previously saved counter value, or start from 0
    private var counter: Int = stateKeeper.consume(
        key = "COUNTER",
        strategy = Int.serializer()
    ) ?: 0

    init {
        // Save the current counter whenever Android asks for it
        stateKeeper.register(
            key = "COUNTER",
            strategy = Int.serializer()
        ) { counter }
    }

    fun increment() { counter++ }
}

```

The `consume`/`register` calls map directly to Android's `SavedStateRegistry` via `DefaultJetpackComponentContext`, ensuring the counter value survives process termination.

### Simplified State Handling with the saveable Delegate

Decompose provides an experimental `saveable` delegate that automatically handles both consumption and registration:

```kotlin
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.statekeeper.saveable
import kotlinx.serialization.Serializable

class UserComponent(componentContext: ComponentContext) : ComponentContext by componentContext {

    // `saveable` automatically wires consume + register
    private var userName: String by saveable(
        serializer = String.serializer(),
        init = { "" }                // default when no saved state
    )
}

```

The delegate internally registers the same state-keeper entry that ends up in the Android bundle, reducing boilerplate while maintaining process-death survival.

### Preserving Complex Objects with InstanceKeeper

For ViewModel-like objects that must survive both configuration changes and process death, combine `InstanceKeeper` with `StateKeeper`:

```kotlin
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.statekeeper.saveable
import kotlinx.serialization.Serializable

class CartComponent(componentContext: ComponentContext) : ComponentContext by componentContext {

    private val cart = instanceKeeper.getOrCreate {
        CartStatefulEntity(savedState = stateKeeper.consume("CART", CartState.serializer()))
    }

    init {
        stateKeeper.register("CART", CartState.serializer()) { cart.state }
    }
}

class CartStatefulEntity(savedState: CartState?) : InstanceKeeper.Instance {
    var state: CartState = savedState ?: CartState()
    private set

    @Serializable data class CartState(val items: List<String> = emptyList())
}

```

The retained entity survives configuration changes through `InstanceKeeper` and process death through `StateKeeper`, providing comprehensive state persistence.

## Serialization and Lifecycle Flow

Decompose relies on `kotlinx.serialization` to convert arbitrary data classes into a format storable in an Android `Bundle`. The complete lifecycle across process death follows this sequence:

1. **Component Initialization**: The component calls `stateKeeper.consume()` with a serializer (e.g., `Int.serializer()` or `MyState.serializer()`).
2. **State Restoration**: If Android provides a saved bundle from a previous process, `DefaultJetpackComponentContext` deserializes it and returns the object; otherwise, the component uses a default value.
3. **Registration**: The component calls `stateKeeper.register()`, providing a lambda that returns the current state. This lambda is invoked when Android requests state preservation.
4. **Process Death**: When the system kills the process, Android calls the registered callbacks. `DefaultJetpackComponentContext` triggers `performSave`, serializing the state into a `Bundle`.
5. **Recreation**: When the user returns and the activity recreates, `DefaultJetpackComponentContext` calls `performRestore` with the persisted bundle, making the saved state available via `consume()` on the next component initialization.

Developers can opt out of this behavior by passing `serializer = null` when creating navigation stacks or panels, which disables `StateKeeper` registration entirely.

## Summary

- **Decompose** treats state preservation as a core feature available on all platforms, with Android-specific integration via Jetpack Saved State.
- **`ComponentContext`** extends `StateKeeperOwner`, exposing `stateKeeper` for manual `consume`/`register` operations or the `saveable` delegate.
- **`DefaultJetpackComponentContext`** bridges the generic `StateKeeper` to Android's `SavedStateRegistry` using `SavedStateSerializer`, ensuring bundles survive process death.
- **`kotlinx.serialization`** powers the conversion between Kotlin objects and Android-persistable bundles.
- **Opt-out** is available by passing `null` serializers, though the default behavior automatically handles process death survival.

## Frequently Asked Questions

### How does Decompose differ from ViewModel's SavedStateHandle?

Decompose provides a platform-agnostic `StateKeeper` API that works identically across Android, iOS, Desktop, and Web. While `SavedStateHandle` is Android-specific and tied to the ViewModel lifecycle, Decompose's `stateKeeper` integrates with Android's `SavedStateRegistry` only when running on Android via `DefaultJetpackComponentContext`, offering the same process-death survival without platform lock-in.

### Can I use Decompose state preservation without kotlinx.serialization?

No. Decompose relies on `kotlinx.serialization` to convert state objects into a format that can be stored in Android bundles. You must provide a serializer (e.g., `MyState.serializer()`) when calling `stateKeeper.consume()` or `stateKeeper.register()`. The library does not support Java Serializable or Parcelable directly, though you can write custom serializers if needed.

### What happens if I don't register any state with StateKeeper?

If you do not call `stateKeeper.register()` (or use the `saveable` delegate which registers automatically), your component state will not be persisted across process death. The component will reinitialize with default values when the system recreates your activity. Configuration changes (like rotation) may still preserve state through `InstanceKeeper` if you use retained instances, but process death requires explicit `StateKeeper` registration.

### Is state preservation automatic for navigation stacks in Decompose?

Yes, but only if you provide serializers. When creating navigation stacks using `childStack` or `childPanels`, Decompose automatically wires the navigation state through `StateKeeper` if you pass non-null serializers for the configuration objects. If you pass `serializer = null`, the library disables state preservation for that navigation entity, and the stack will reset to initial state after process death.