Android Development

Issue Tracker Android Updates Android Home Home Contact

Android Design Patterns

MVC (Model-View-Controller)

MVC (Model–View–Controller) separates the application into three components: Model (data), View (UI), and Controller (logic), aiming to decouple presentation from business logic.

  • Model: Handles data and business logic. It can fetch data from a network, local database, or file system.
  • View: Displays data to the user. In Android, this could be an Activity, Fragment, or custom view. It forwards user actions (e.g., button clicks) to the Controller.
  • Controller: Coordinates between the View and Model. It interprets user actions, updates the Model, and modifies the View accordingly. In Android, often the Activity or Fragment acts as both Controller and View, which can blur responsibilities.

Typical Data Flow

From View to Model:
User interacts with UI (View) → Controller receives event → Controller updates Model or requests data.

From Model to View:
Model sends data back to Controller → Controller updates the View (e.g., refresh UI with new data).

In Android, the Controller is often tightly coupled with the View (e.g., the same Activity), which can lead to bloated classes and harder testing.


MVP (Model-View-Presenter)

MVP (Model–View–Presenter) improves on MVC by strictly decoupling View and Presenter via interfaces. It makes UI logic more testable and maintainable, as Presenters don’t rely on Android framework classes.

  • Model: Handles data operations, just like in MVC. This includes networking, database access, and business logic.
  • View: Only displays data and captures user input. It exposes an interface (e.g., MyViewInterface) that the Presenter calls to update the UI. The View passes user interactions to the Presenter without containing logic.
  • Presenter: Contains all presentation logic. It receives user actions from the View, fetches or updates data via the Model, and tells the View what to display via the View interface.

Typical Data Flow

From View to Model:
User action in View → View delegates to Presenter → Presenter calls appropriate method on Model (e.g., fetch API or update DB).

From Model to View:
Model returns data → Presenter processes it → Presenter updates View via View interface (e.g., view.showResult(data)).

In MVP, the View has no direct access to the Model, and the Presenter is fully decoupled from Android framework classes, making it easier to write unit tests and mock UI dependencies.


MVVM (Model-View-ViewModel)

MVVM (Model–View–ViewModel) is a modern, lifecycle-aware architecture used in Android to separate concerns and organize code for maintainability and testability. It promotes a reactive data flow between the layers and is highly compatible with tools like LiveData and Kotlin Flow.

  • Model: Represents the data layer. This includes your data classes, repositories, remote APIs (e.g., Retrofit), and local storage (e.g., Room database). It encapsulates business logic and fetches or updates data from the appropriate source (network or database).
  • ViewModel: Serves as the middle layer between the View and Model. It holds UI-related data and exposes it to the View using observable data holders like LiveData or Flow. It receives user actions from the View (e.g., button clicks), makes calls to the Repository (Model layer), and updates the UI state accordingly. ViewModels are lifecycle-aware and survive configuration changes like screen rotations.
  • View: Typically an Activity or Fragment that only observes the ViewModel's exposed data and reacts to UI changes. It sends user input to the ViewModel but does not handle business logic directly. The View observes LiveData or Flow through collectors (e.g., collect() in Compose or observe() in XML-based views) in a lifecycle-safe manner, preventing memory leaks.
  • Repository: Part of the Model layer, a Repository acts as a single source of truth. It abstracts where the data comes from (remote or local) and provides a clean API to the ViewModel. It may include suspend functions or return flows depending on how data is being retrieved or updated.

Typical Data Flow

From View to Model:
User action in the View (e.g., tapping a button) → triggers a method in ViewModel → ViewModel calls Repository → Repository fetches data via API (e.g., Retrofit) or local DB → returns result.

From Model to View:
Repository returns data to ViewModel → ViewModel updates exposed LiveData or Flow → View observes and reacts (e.g., updates a list).

LiveData vs Flow

  • LiveData: Lifecycle-aware, simple, great for UI binding. It automatically cleans up observers tied to lifecycle owners (Activities/Fragments). Best for one-time events or basic state observation.
  • Flow: A Kotlin construct supporting more advanced use cases (transforms, retries, pagination). Needs to be collected explicitly and is not lifecycle-aware by default — but StateFlow and SharedFlow provide lifecycle-safe wrappers for state and event delivery, especially when using Jetpack Compose or modern UIs.

Lifecycle Safety

Whether using LiveData or Flow, it is essential to use lifecycle-aware collection techniques to avoid memory leaks. For example:

  • LiveData: Use observe(viewLifecycleOwner) inside Fragments to tie observation to lifecycle.
  • Flow: In Compose, use collectAsState(); in XML-based UIs, use lifecycleScope.launchWhenStarted { flow.collect { ... } }.

MVVM enables a clean separation of UI and business logic. The ViewModel exposes data via LiveData or Flow, observes lifecycle changes, and bridges the View with the underlying data logic. This design minimizes memory leaks, supports configuration changes like screen rotation, and simplifies UI responsiveness by leveraging reactive, observable data streams.


MVI (Model-View-Intent)

MVI is a reactive design pattern that simplifies state management by treating the application's state as a single source of truth. It emphasizes immutability and unidirectional data flow.

  • Model: Represents the state of the application and business logic.
  • View: A stateless component that renders the UI based on the Model's state and sends user actions (Intents) to the Intent layer.
  • Intent: Processes user actions and updates the Model, often by interacting with a backend or other data sources.

Key Characteristics:

  • Unidirectional data flow ensures consistency and predictability.
  • The entire UI is represented as a single state object, making debugging easier.
  • Commonly used with reactive libraries like RxJava or Kotlin Coroutines with StateFlow.

When to Use:

  • Apps requiring complex state management.
  • Projects benefiting from clear separation of concerns and immutability.
  • Real-time applications like chat or live updates.

Clean Architecture

Clean Architecture focuses on organizing code into layers to achieve high testability, maintainability, and separation of concerns. The dependency rule states that inner layers should not depend on outer layers.

  • Entities: Core business logic and rules.
  • Use Cases/Interactors: Application-specific business logic.
  • Interface Adapters: Converts data between use cases and the external world (e.g., Presenters, ViewModels).
  • Frameworks & Drivers: External dependencies like databases, UI frameworks, and APIs.

Key Characteristics:

  • Promotes testability by isolating business logic from external dependencies.
  • Each layer is independent and focuses on a specific responsibility.
  • Decoupling allows for easier modifications and scalability.

When to Use:

  • Large, scalable applications with long-term maintenance requirements.
  • Projects with complex domain logic.
  • Teams that prioritize unit testing and clean separation of concerns.

Comparing MVI and Clean Architecture

Aspect MVI Clean Architecture
Focus State management and immutability Layered architecture for codebase organization
Preferred For Real-time apps, state-driven UI Large, complex applications with scalable requirements
Ease of Testing Simplifies testing with single-state representation Testable by isolating logic in independent layers
Complexity Moderate (depends on implementation) Higher complexity due to additional layers