How to Use ViewModelScope Properly in Kotlin is one of the most essential topics for Android developers who are working with Jetpack libraries. If you are building Android applications using MVVM (Model-View-ViewModel) architecture, then understanding viewModelScope
is critical to avoid memory leaks, manage coroutines effectively, and ensure smooth lifecycle management.

What is ViewModelScope in Kotlin?
viewModelScope
is a CoroutineScope provided by the AndroidX lifecycle library inside the ViewModel
class. It automatically gets canceled when the ViewModel
is cleared, making it a perfect scope to launch coroutines that should live as long as the ViewModel does.
In simple terms, you don’t need to manually cancel coroutines when the screen is destroyed, because viewModelScope
handles this for you.
Why Use ViewModelScope?
- Automatic lifecycle management: Coroutines get canceled when the ViewModel is destroyed.
- Less boilerplate: No need to manually create or cancel CoroutineScopes.
- Better memory management: Prevents leaks caused by unfinished coroutines.
- Clean architecture: Keeps business logic inside ViewModel instead of Activity or Fragment.
How to Use ViewModelScope in Kotlin (Step by Step)
1. Add dependencies
First, make sure you have the following dependencies in your build.gradle
file:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
2. Create a ViewModel with ViewModelScope
Here’s an example ViewModel that fetches data from a repository using viewModelScope
:
class UserViewModel( private val repository: UserRepository ) : ViewModel() { private val _userData = MutableLiveData<User>() val userData: LiveData<User> get() = _userData fun getUser(userId: Int) { viewModelScope.launch { try { val user = repository.fetchUser(userId) _userData.postValue(user) } catch (e: Exception) { Log.e("UserViewModel", "Error fetching user", e) } } } }
3. Repository Example
The repository simulates fetching data from an API or local database:
class UserRepository { suspend fun fetchUser(userId: Int): User { delay(2000) // Simulating network call return User(id = userId, name = "John Doe", email = "john@example.com") } } data class User( val id: Int, val name: String, val email: String )
4. Using ViewModel in Activity
In your Activity or Fragment, you can observe the LiveData:
class MainActivity : AppCompatActivity() { private val viewModel: UserViewModel by viewModels { UserViewModelFactory(UserRepository()) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel.userData.observe(this) { user -> findViewById<TextView>(R.id.textView).text = "Name: ${user.name}" } // Fetch user viewModel.getUser(1) } }
Best Practices for Using ViewModelScope
- Use structured concurrency: Always launch coroutines inside
viewModelScope
instead of creating new GlobalScope. - Handle exceptions properly: Use
try/catch
orCoroutineExceptionHandler
. - Expose LiveData or StateFlow: Keep UI updates reactive and avoid direct callbacks.
- Use withContext for background threads: When doing I/O operations, switch to
Dispatchers.IO
.
viewModelScope.launch(Dispatchers.IO) { val data = repository.fetchUser(2) withContext(Dispatchers.Main) { _userData.value = data } }
Common Mistakes to Avoid
- Using
GlobalScope.launch
instead ofviewModelScope
. - Not handling exceptions in coroutines.
- Blocking the main thread with heavy operations.
- Not canceling manually created scopes when ViewModel is cleared.
Comparison: GlobalScope vs ViewModelScope
Aspect | GlobalScope | ViewModelScope |
---|---|---|
Lifecycle Awareness | No | Yes (Canceled when ViewModel is cleared) |
Memory Management | Risk of leaks | Safe, managed automatically |
Best Use Case | Background jobs not tied to UI | UI-related coroutines in MVVM |
Conclusion
Using viewModelScope
in Kotlin is the best way to handle coroutines inside your ViewModel. It simplifies lifecycle management, reduces boilerplate, and helps you write cleaner code. By following best practices, you can ensure that your Android applications remain responsive, stable, and memory-efficient.
If you are learning more about coroutines in Android, you can also check the official documentation from Android Developers.