Skip to content

How to Fix Android ViewModel Not Persisting State

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about How to Fix Android ViewModel Not Persisting State. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your ViewModel loses data when the app is backgrounded and killed:

class MyViewModel : ViewModel() {
    var count = 0  // reset after process death
}

After process recreation, count is back to 0. The ViewModel survives configuration changes but not process death.

Quick Fix

Step 1: Use SavedStateHandle in the ViewModel

class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {

    val count = savedStateHandle.getLiveData<Int>("count")

    fun increment() {
        count.value = (count.value ?: 0) + 1
    }
}

Add a Factory or use Hilt/AndroidX assisted injection:

class MyViewModelFactory(
    private val owner: SavedStateRegistryOwner,
    private val defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
    override fun <T : ViewModel> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
        return MyViewModel(handle) as T
    }
}

Step 2: Register the ViewModel correctly

// Scoped to Navigation back stack entry
val viewModel: MyViewModel by viewModels()

// Scoped to Activity (shared across fragments)
val viewModel: MyViewModel by activityViewModels()

Using by viewModels() inside a NavBackStackEntry scopes the ViewModel to that destination. If you navigate away and back, a new ViewModel is created.

Step 3: Do not store large data in ViewModel directly

// Wrong - bitmap survives rotation but not process death
class MyViewModel : ViewModel() {
    var bitmap: Bitmap? = null
}

// Right - persist to disk or SavedStateHandle for critical state
class MyViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    val imageUri = savedStateHandle.get<String>("image_uri")
}

Step 4: Enable ViewModel state with Jetpack Navigation

<argument
    android:name="itemId"
    android:defaultValue="0"
    app:argType="integer" />

Navigation stores arguments in SavedStateHandle automatically:

class DetailViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    val itemId = savedStateHandle.get<Int>("itemId") ?: 0
}

Step 5: Check ViewModel scope

Scope Lifecycle
Activity Until Activity finishes
Fragment Until Fragment detaches
NavBackStackEntry Until entry is popped

ViewModel is never garbage collected during its scope. It is cleared when the scope's LifecycleOwner is permanently destroyed.

Prevention

  • Use SavedStateHandle for any state that must survive process death.
  • Avoid storing complex objects directly in ViewModel fields.
  • Use StateFlow or LiveData exposed from the ViewModel.

Common Mistakes with viewmodel error

  1. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
  2. Non-exhaustive pattern matches that compile with warnings then crash at runtime
  3. Misunderstanding that String is [Char] with poor performance for large text operations

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Does ViewModel survive process death by default?

No. ViewModel survives configuration changes (rotation) but is destroyed when the process is killed. Use SavedStateHandle to survive both.

What is the difference between LiveData and StateFlow in ViewModel?

Both emit state changes. LiveData is lifecycle-aware (stops emitting when the Observer is inactive). StateFlow requires repeatOnLifecycle for lifecycle safety. Prefer StateFlow + repeatOnLifecycle for newer codebases.

Can I use ViewModel with Compose?

Yes. Use viewModel() in a Composable to get a ViewModel scoped to the nearest ViewModelStoreOwner (Activity, Fragment, or NavBackStackEntry).

DodaTech Tool Reference

Doda Browser's Memory Profiler can track ViewModel instances and detect leaks from incorrectly scoped ViewModel references.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro