Why MVVM Is the Standard in 2026
MVVM is what Google recommends and what every serious Android codebase uses. Going into an interview in 2026 talking about putting API calls in your Activity will cost you the job. This guide builds the pattern from scratch so you understand it, not just copy it.
The Three Layers
- View (Activity/Fragment) — only observes state, never fetches data
- ViewModel — holds UI state, calls Repository, survives config changes
- Repository — single source of truth, handles network and cache decisions
Setting Up Retrofit
object ApiClient {
private const val BASE_URL = "https://api.example.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service: ApiService = retrofit.create(ApiService::class.java)
}
The Repository
class UserRepository {
private val api = ApiClient.service
suspend fun getUser(id: Int): Result<UserResponse> {
return try {
val response = api.getUser(id)
if (response.isSuccessful) Result.success(response.body()!!)
else Result.failure(Exception("Error: ${response.code()}"))
} catch (e: Exception) { Result.failure(e) }
}
}
The ViewModel
class UserViewModel(
private val repository: UserRepository = UserRepository()
) : ViewModel() {
private val _state = MutableStateFlow<UiState<UserResponse>>(UiState.Loading)
val state = _state.asStateFlow()
fun loadUser(id: Int) {
viewModelScope.launch {
_state.value = UiState.Loading
repository.getUser(id)
.onSuccess { _state.value = UiState.Success(it) }
.onFailure { _state.value = UiState.Error(it.message ?: "Error") }
}
}
}
Observing in Activity
lifecycleScope.launch {
viewModel.state.collect { state ->
when (state) {
is UiState.Loading -> showLoader()
is UiState.Success -> showUser(state.data)
is UiState.Error -> showError(state.message)
}
}
}
Common Mistakes
- Calling API directly in Activity — always through ViewModel → Repository
- Using GlobalScope.launch — use viewModelScope so it cancels correctly
- Not handling errors in Repository — always wrap network calls in try/catch
- Exposing MutableStateFlow publicly — always expose as StateFlow
Ready to build this for real?
This is what we teach hands-on at Cloudemy Edge, Lucknow —
real code, real projects, real career outcomes.
Android Training in Lucknow →