Stopping the State Reset: Managing Configuration Changes with Android ViewModel
Learn how to use Android ViewModel to prevent data loss during screen rotations and configuration changes, while avoiding common memory leak pitfalls.
14 Jul 2025, 22:20 UTC

The Rotation Reset Problem
In Android development, a common frustration is the "reset" that occurs when a user rotates their device or changes system language. By default, Android destroys and recreates the Activity to apply new resources. If you are holding a user's form input, a scroll position, or a fetched list of data in a simple variable within the Activity, that data vanishes the moment the screen flips.
The core challenge is that the Activity lifecycle is too volatile for data storage. The solution is the ViewModel, a component designed to outlive the Activity instance and persist data across these configuration changes.
How ViewModel Survives the Lifecycle
A ViewModel does not live inside the Activity; instead, it is managed by the ViewModelStore. When an Activity is destroyed due to a configuration change, the ViewModelStore retains the ViewModel instance in memory. When the new Activity instance is created, it requests the ViewModel again from the provider, receiving the exact same instance that was used previously.
This mechanism relies on the ViewModelStoreOwner interface, which the Activity implements. This link ensures that the ViewModel is only fully cleared when the Activity is finished permanently (for example, the user presses the back button or finish() is called), rather than just being recreated.
Practical Implementation: A State-Preserving Counter
To implement this, you need the Android Architecture Components library. The following example demonstrates a simple counter that persists through rotation. This assumes you are using Kotlin and the androidx.lifecycle:lifecycle-viewmodel-ktx dependency.
// The ViewModel handles the data and logic
class CounterViewModel : ViewModel() {
// Use MutableStateFlow to allow the UI to observe changes
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
fun increment() {
_count.value += 1
}
}
// The Activity observes the ViewModel
class CounterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Access the ViewModel via the ViewModelProvider
val viewModel: CounterViewModel by viewModels()
val textView = findViewById<TextView>(R.id.counterText)
val button = findViewById<Button>(R.id.incrementButton)
// Collect the flow to update the UI
lifecycleScope.launch {
viewModel.count.collect { value ->
textView.text = "Count: $value"
}
}
button.setOnClickListener { viewModel.increment() }
}
}Critical Constraints and Memory Leaks
The power of the ViewModel comes with a strict rule: Never store a reference to a View, Fragment, or Activity inside a ViewModel.
Because the ViewModel outlives the Activity, holding a reference to a TextView or a Context prevents the old Activity from being garbage collected after a rotation. This creates a memory leak. If you need a context for system services (such as accessing a database or shared preferences), inherit from AndroidViewModel, which provides access to the Application context. That context lives for the entire duration of the app process and is safe to hold.
ViewModel vs. Persistent Storage
It is important to distinguish between a configuration change and process death. A ViewModel survives a rotation, but it will not survive if the Android OS kills the app process to reclaim memory while it is in the background.
| Scenario | ViewModel Status | Recommended Solution |
|---|---|---|
| Screen Rotation | Preserved | ViewModel |
| Language Change | Preserved | ViewModel |
| System Process Death | Cleared | SavedStateHandle or Room/DataStore |
| User Closes App | Cleared | Room/DataStore |
Verification and Testing
To verify your implementation is working correctly, follow these steps:
- Manual Test: Launch the app, increment the counter, and rotate the device. The number should remain unchanged.
- Lifecycle Check: Override
onCleared()in your ViewModel and add a log statement. Navigate back from the Activity; you should see the log, confirming the ViewModel was destroyed only when the Activity was finished. - Memory Profiling: Use the Android Studio Memory Profiler. Rotate the device several times and trigger a Garbage Collection (GC). If the number of Activity instances keeps climbing, you have a memory leak in your ViewModel.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.