One of the most common challenges in Android development is keeping your UI data intact when the device configuration changes — a screen rotation, keyboard appearing, or language switch. Without the right approach, your app loses data, shows stale UI, or crashes entirely.
ViewModels are the solution. Part of Android's MVVM (Model-View-ViewModel) architecture, they store and manage UI-related data independently of the Activity or Fragment lifecycle. In this guide you'll build your first ViewModel step by step, understand when and why to use it, and learn best practices that apply to real-world apps.
What is a ViewModel?
A ViewModel is an architecture component designed to store and manage UI-related data in a lifecycle-aware way. It lives as long as its associated Activity or Fragment is alive — and crucially, it survives configuration changes like screen rotation.
Without ViewModel, every time you rotate your screen the Activity is destroyed and recreated, wiping out any data you were holding in it. With ViewModel, the data stays in memory and your UI simply re-attaches to the existing ViewModel.
- Configuration Change Survivor: Persists through rotation, keyboard changes, and language switches
- Lifecycle-Aware: Automatically cleaned up when the Activity is permanently finished — no memory leaks
- Separation of Concerns: Keeps UI logic out of Activity/Fragment, making code cleaner and easier to test
- Works with LiveData & StateFlow: Exposes data reactively so the UI always stays up to date — learn more in our LiveData and ViewModel guide
The Problem: Configuration Changes
When a configuration change occurs, Android destroys and recreates your Activity. This triggers the full lifecycle: onDestroy() → onCreate(). Any data stored directly in the Activity is lost.
Common configuration changes that cause this:
- Screen rotation — portrait to landscape and vice versa
- Keyboard visibility — soft keyboard appearing or disappearing
- Language/Locale change — switching between languages
- Dark/Light mode toggle — theme changes at runtime
ViewModel solves this by living outside the Activity's lifecycle scope. For a deeper look at how this works, read our ViewModel Lifecycle guide.
Building Your First ViewModel — Step by Step
Step 1: Add the dependency
Add the ViewModel KTX dependency to your build.gradle (app) file:
// build.gradle (app)
dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0"
implementation "androidx.activity:activity-ktx:1.9.0"
}
Step 2: Create your ViewModel class
Create a new Kotlin file MyViewModel.kt and extend ViewModel(). Use MutableLiveData privately and expose immutable LiveData publicly — this prevents the UI from directly modifying state.
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
// Private mutable — only ViewModel modifies this
private val _myData = MutableLiveData<String>()
// Public immutable — UI observes this
val myData: LiveData<String> = _myData
fun loadData() {
// Simulate loading data (e.g. from a repository)
_myData.value = "Hello from ViewModel!"
}
}
Step 3: Access the ViewModel in your Activity
Use the by viewModels() Kotlin property delegate — the simplest and recommended way to get a ViewModel in an Activity. It automatically scopes the ViewModel to the Activity's lifecycle.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
class MainActivity : AppCompatActivity() {
// Scoped to this Activity — survives rotation automatically
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Observe LiveData — updates UI whenever data changes
viewModel.myData.observe(this) { data ->
binding.tvMessage.text = data
}
// Trigger data load
viewModel.loadData()
}
}
Step 4: Access ViewModel in a Fragment
In a Fragment, use by viewModels() for a Fragment-scoped ViewModel, or by activityViewModels() to share the same ViewModel instance with the host Activity — useful for sharing data between fragments.
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels
class MyFragment : Fragment() {
// Fragment-scoped ViewModel (separate instance from Activity)
private val fragmentViewModel: MyViewModel by viewModels()
// Activity-scoped ViewModel (shared with Activity and other Fragments)
private val sharedViewModel: MyViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
sharedViewModel.myData.observe(viewLifecycleOwner) { data ->
// Update UI
}
}
}
Step 5: Update your layout
Make sure your activity_main.xml has a TextView to display the data:
<TextView
android:id="@+id/tvMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loading..."
android:textSize="18sp"/>
by viewModels() vs ViewModelProvider — What's the Difference?
Both achieve the same result but by viewModels() is the modern, concise Kotlin way:
| Approach | Code | Recommended? |
|---|---|---|
| by viewModels() | private val vm: MyVM by viewModels() |
✅ Yes — modern Kotlin |
| ViewModelProvider | ViewModelProvider(this)[MyVM::class.java] |
⚠️ Use when passing custom factory |
| by activityViewModels() | private val vm: MyVM by activityViewModels() |
✅ Yes — for sharing between fragments |
Best Practices
- Never hold Context or View references in a ViewModel — it outlives the Activity and will cause memory leaks. Use
AndroidViewModelif you genuinely need Application context. - Expose immutable data — keep
MutableLiveDataorMutableStateFlowprivate, exposeLiveDataorStateFlowpublicly. - Use LiveData for simple state, StateFlow for complex state — learn the difference in our ViewModel State Management guide.
- Use viewModelScope for coroutines — it automatically cancels coroutines when the ViewModel is cleared. Learn more in our Kotlin Coroutines guide.
- One ViewModel per screen — don't share a single ViewModel across unrelated screens. Use
by activityViewModels()only for genuinely shared data between fragments on the same screen.
Frequently Asked Questions
Does ViewModel survive process death (app killed by system)?
No — ViewModel only survives configuration changes. If the system kills the process due to low memory, ViewModel data is lost. For that use case, combine ViewModel with SavedStateHandle. See our State Management guide for details.
Can I use ViewModel with Jetpack Compose?
Yes — use the viewModel() composable from androidx.lifecycle:lifecycle-viewmodel-compose to get a ViewModel in any composable function.
When is a ViewModel destroyed?
When the Activity is permanently finished (user presses back or calls finish()). Not during rotation or configuration changes. Read more about the ViewModel lifecycle here.
Can I pass constructor arguments to a ViewModel?
Yes — use a ViewModelFactory or Hilt's @HiltViewModel with @Inject constructor for dependency injection.
- ViewModels survive configuration changes — no more data loss on rotation
- Use by viewModels() in Activity, by activityViewModels() to share between fragments
- Always expose immutable LiveData/StateFlow to the UI
- Never hold Context or View references inside a ViewModel
- Use viewModelScope for coroutines inside ViewModel
- For state that survives process death, use SavedStateHandle
Thansks
ReplyDelete