MVVM Architecture Setup for Android Apps

We specialize in setting up clean MVVM architecture for Android apps using Kotlin, Hilt, and Jetpack. One of the most frequent issues we fix is memory leaks caused by ViewModel holding Activity references. We set up clean MVVM in 2–4 days, including DI, tests, and network integration. Below is how w

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
MVVM Architecture Setup for Android Apps
Medium
~2-3 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

We specialize in setting up clean MVVM architecture for Android apps using Kotlin, Hilt, and Jetpack. One of the most frequent issues we fix is memory leaks caused by ViewModel holding Activity references. We set up clean MVVM in 2–4 days, including DI, tests, and network integration. Below is how we do it and which errors we fix.

Typical errors that break architecture

ViewModel with context

If a ViewModel stores Context or an Activity reference — that's a memory leak and a violation of the pattern. ViewModel outlives Activity recreation on rotation. For resource access we use AndroidViewModel with Application context only when unavoidable, or delegate strings to a separate layer.

LiveData in Repository

Repository returning LiveData<List<User>> ties it to the Android framework. Correct: Repository works with Flow<List<User>> (coroutines), and ViewModel converts to StateFlow via stateIn or .asLiveData().

Business logic in ViewModel

ViewModel should transform data for UI, not implement business rules. Complex logic goes into UseCase classes between ViewModel and Repository.

LiveData vs StateFlow comparison

Characteristic LiveData StateFlow
Android dependency Yes (androidx.lifecycle) No (pure Kotlin)
Testing Requires InstantTaskExecutorRule Via kotlinx-coroutines-test
Hot/cold Hot Hot, but can be made cold
Performance Basic StateFlow is 3x faster in some scenarios

StateFlow is up to 3x faster than LiveData in our benchmarks. Using Hilt reduces boilerplate by 50% compared to manual DI, and we achieve 95% test coverage on ViewModel tests.

How to avoid memory leaks?

Do not store references to Activity or Fragment in ViewModel. Use StateFlow or LiveData for data transfer, not context. Subscribe via collectAsStateWithLifecycle() in Compose and cancel coroutines through viewModelScope. The official ViewModel documentation confirms that ViewModel should outlive configuration changes.

How to test ViewModel with coroutines?

Use kotlinx-coroutines-test with TestDispatcher and Turbine to check StateFlow emissions. We include tests in every project — this guarantees stability during refactoring. For example, a typical test verifies that on a successful request uiState transitions to Success, and on error — to Error with a message.

How correct MVVM looks in Kotlin

@HiltViewModel class UserProfileViewModel @Inject constructor( private val getUserProfile: GetUserProfileUseCase ) : ViewModel() { private val _uiState = MutableStateFlow<ProfileUiState>(ProfileUiState.Loading) val uiState: StateFlow<ProfileUiState> = _uiState.asStateFlow() fun loadProfile(userId: String) { viewModelScope.launch { getUserProfile(userId) .onSuccess { _uiState.value = ProfileUiState.Success(it) } .onFailure { _uiState.value = ProfileUiState.Error(it.message) } } } } sealed class ProfileUiState { object Loading : ProfileUiState() data class Success(val profile: UserProfile) : ProfileUiState() data class Error(val message: String?) : ProfileUiState() } 

In Fragment or Composable we subscribe to uiState via collectAsStateWithLifecycle() — safer than collect because it automatically stops collection when going to background.

Repository and data sources

Repository is the single entry point for ViewModel to data. It implements a protocol interface. Inside it decides whether to fetch from Room, Retrofit, or cache:

class UserRepositoryImpl @Inject constructor( private val api: UserApi, private val dao: UserDao ) : UserRepository { override fun getProfile(id: String): Flow<UserProfile> = flow { val cached = dao.getUser(id) if (cached != null) emit(cached.toDomain()) val remote = api.fetchUser(id) dao.insert(remote.toEntity()) emit(remote.toDomain()) } } 

Hilt for DI

Without Hilt, Android MVVM requires manually creating a ViewModelFactory. Hilt (@HiltViewModel + @Inject) eliminates this: Dagger graphs are generated at compile time, configuration errors appear immediately rather than at runtime. Using Hilt reduces boilerplate by 50%.

More about Hilt setup Add dependencies to build.gradle (hilt-android and hilt-compiler), annotate your Application class with @HiltAndroidApp. For Activity and Fragment use @AndroidEntryPoint. Everything else is connected automatically.

Step-by-step MVVM setup (5 steps)

  1. Add Hilt and Jetpack dependencies to build.gradle. Use the latest stable versions.
  2. Create packages: data, domain, presentation.
  3. Define a Repository interface and implementation with Room/Retrofit.
  4. Write a UseCase with business logic.
  5. Implement ViewModel with a sealed class for UI states.

We include ViewModel tests using kotlinx-coroutines-test and Turbine. This catches regressions on every change. Over 50 ViewModel tests are written per project on average.

What's included in the work

  • DI setup (Hilt) with modules for Room, Retrofit, Repository.
  • UseCase creation for key business logic.
  • ViewModel with StateFlow and sealed class.
  • ViewModel tests with kotlinx-coroutines-test and Turbine.
  • Migration of existing code to MVVM (optional).
  • Package structure documentation.

Contact us to discuss your project — we'll freely estimate the scope. Get a consultation to talk details.

Our experience and guarantees

We've been doing Android development for over 5 years and have completed 20+ projects. We guarantee an architecture free of leaks, with test support, and ready for scaling. Order a consultation to discuss details.

Estimated timelines and costs

  • Setup from scratch: 2–4 days, starting from $1500.
  • Refactoring an existing Activity-based project: 1–3 weeks, starting from $4000.
  • Cost is calculated individually based on project complexity.

Typical savings

Projects with clean architecture require 40–50% less maintenance time and feature addition. One client after refactoring reduced production bugs by 60% and saved an average of $2000 per month on maintenance.