This guide encompasses best practices and recommended architecture for building robust, high-quality apps.
Mobile app user experiences
A typical Android app contains multiple app components, including activities, fragments, services, content providers, and broadcast receivers. You declare most of these app components in your app manifest. The Android OS then uses this file to decide how to integrate your app into the device's overall user experience. Given that a typical Android app might contain multiple components and that users often interact with multiple apps in a short period of time, apps need to adapt to different kinds of user-driven workflows and tasks.
Keep in mind that mobile devices are also resource-constrained, so at any time, the operating system might kill some app processes to make room for new ones.
Given the conditions of this environment, it's possible for your app components to be launched individually and out-of-order, and the operating system or user can destroy them at any time. Because these events aren't under your control, you shouldn't store or keep in memory any application data or state in your app components, and your app components shouldn't depend on each other.
Common architectural principles
If you shouldn't use app components to store application data and state, how should you design your app instead?
As Android apps grow in size, it's important to define an architecture that allows the app to scale, increases the app's robustness, and makes the app easier to test.
An app architecture defines the boundaries between parts of the app and the responsibilities each part should have. In order to meet the needs mentioned above, you should design your app architecture to follow a few specific principles.
Separation of concerns
The most important principle to follow is separation of
concerns.
It's a common mistake to write all your code in an
Activity
or a
Fragment
. These UI-based classes should
only contain logic that handles UI and operating system interactions. By keeping
these classes as lean as possible, you can avoid many problems related to the
component lifecycle, and improve the testability of these classes.
Keep in mind that you don't own implementations of Activity
and Fragment
;
rather, these are just glue classes that represent the contract between the
Android OS and your app. The OS can destroy them at any time based on user
interactions or because of system conditions like low memory. To provide a
satisfactory user experience and a more manageable app maintenance experience,
it's best to minimize your dependency on them.
Drive UI from data models
Another important principle is that you should drive your UI from data models, preferably persistent models. Data models represent the data of an app. They're independent from the UI elements and other components in your app. This means that they are not tied to the UI and app component lifecycle, but will still be destroyed when the OS decides to remove the app's process from memory.
Persistent models are ideal for the following reasons:
Your users don't lose data if the Android OS destroys your app to free up resources.
Your app continues to work in cases when a network connection is flaky or not available.
If you base your app architecture on data model classes, you make your app more testable and robust.
Single source of truth
When a new data type is defined in your app, you should assign a Single Source of Truth (SSOT) to it. The SSOT is the owner of that data, and only the SSOT can modify or mutate it. To achieve this, the SSOT exposes the data using an immutable type, and to modify the data, the SSOT exposes functions or receive events that other types can call.
This pattern brings multiple benefits:
- It centralizes all the changes to a particular type of data in one place.
- It protects the data so that other types cannot tamper with it.
- It makes changes to the data more traceable. Thus, bugs are easier to spot.
In an offline-first application, the source of truth for application data is typically a database. In some other cases, the source of truth can be a ViewModel or even the UI.
Unidirectional Data Flow
The single source of truth principle is often used in our guides with the Unidirectional Data Flow (UDF) pattern. In UDF, state flows in only one direction. The events that modify the data flow in the opposite direction.
In Android, state or data usually flow from the higher-scoped types of the hierarchy to the lower-scoped ones. Events are usually triggered from the lower-scoped types until they reach the SSOT for the corresponding data type. For example, application data usually flows from data sources to the UI. User events such as button presses flow from the UI to the SSOT where the application data is modified and exposed in an immutable type.
This pattern better guarantees data consistency, is less prone to errors, is easier to debug and brings all the benefits of the SSOT pattern.
Recommended app architecture
This section demonstrates how to structure your app following recommended best practices.
Considering the common architectural principles mentioned in the previous section, each application should have at least two layers:
- The UI layer that displays application data on the screen.
- The data layer that contains the business logic of your app and exposes application data.
You can add an additional layer called the domain layer to simplify and reuse the interactions between the UI and data layers.

Modern App Architecture
This Modern App Architecture encourages using the following techniques, among others:
- A reactive and layered architecture.
- Unidirectional Data Flow (UDF) in all layers of the app.
- A UI layer with state holders to manage the complexity of the UI.
- Coroutines and flows.
- Dependency injection best practices.
For more information, see the following sections, the other Architecture pages in the table of contents, and the recommendations page that contains a summary of the most important best practices.
UI layer
The role of the UI layer (or presentation layer) is to display the application data on the screen. Whenever the data changes, either due to user interaction (such as pressing a button) or external input (such as a network response), the UI should update to reflect the changes.
The UI layer is made up of two things:
- UI elements that render the data on the screen. You build these elements using Views or Jetpack Compose functions.
- State holders (such as ViewModel classes) that hold data, expose it to the UI, and handle logic.

To learn more about this layer, see the UI layer page.
Data layer
The data layer of an app contains the business logic. The business logic is what gives value to your app—it's made of rules that determine how your app creates, stores, and changes data.
The data layer is made of repositories that each can contain zero to many
data sources. You should create a repository class for each different type of
data you handle in your app. For example, you might create a MoviesRepository
class for data related to movies, or a PaymentsRepository
class for data
related to payments.

Repository classes are responsible for the following tasks:
- Exposing data to the rest of the app.
- Centralizing changes to the data.
- Resolving conflicts between multiple data sources.
- Abstracting sources of data from the rest of the app.
- Containing business logic.
Each data source class should have the responsibility of working with only one source of data, which can be a file, a network source, or a local database. Data source classes are the bridge between the application and the system for data operations.
To learn more about this layer, see the data layer page.
Domain layer
The domain layer is an optional layer that sits between the UI and data layers.
The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple ViewModels. This layer is optional because not all apps will have these requirements. You should use it only when needed—for example, to handle complexity or favor reusability.

Classes in this layer are commonly called use cases or interactors. Each use
case should have responsibility over a single functionality. For example, your
app could have a GetTimeZoneUseCase
class if multiple ViewModels rely on time
zones to display the proper message on the screen.
To learn more about this layer, see the domain layer page.
Manage dependencies between components
Classes in your app depend on other classes in order to function properly. You can use either of the following design patterns to gather the dependencies of a particular class:
- Dependency injection (DI): Dependency injection allows classes to define their dependencies without constructing them. At runtime, another class is responsible for providing these dependencies.
- Service locator: The service locator pattern provides a registry where classes can obtain their dependencies instead of constructing them.
These patterns allow you to scale your code because they provide clear patterns for managing dependencies without duplicating code or adding complexity. Furthermore, these patterns allow you to quickly switch between test and production implementations.
We recommend following dependency injection patterns and using the Hilt library in Android apps. Hilt automatically constructs objects by walking the dependency tree, provides compile-time guarantees on dependencies, and creates dependency containers for Android framework classes.
General best practices
Programming is a creative field, and building Android apps isn't an exception. There are many ways to solve a problem; you might communicate data between multiple activities or fragments, retrieve remote data and persist it locally for offline mode, or handle any number of other common scenarios that nontrivial apps encounter.
Although the following recommendations aren't mandatory, in most cases following them makes your code base more robust, testable, and maintainable in the long run:
Don't store data in app components.
Avoid designating your app's entry points—such as activities, services, and broadcast receivers—as sources of data. Instead, they should only coordinate with other components to retrieve the subset of data that is relevant to that entry point. Each app component is rather short-lived, depending on the user's interaction with their device and the overall current health of the system.
Reduce dependencies on Android classes.
Your app components should be the only classes that rely on Android framework
SDK APIs such as Context
, or
Toast
. Abstracting other classes in your
app away from them helps with testability and reduces
coupling
within your app.
Create well-defined boundaries of responsibility between various modules in your app.
For example, don't spread the code that loads data from the network across multiple classes or packages in your code base. Similarly, don't define multiple unrelated responsibilities—such as data caching and data binding—in the same class. Following the recommended app architecture will help you with this.
Expose as little as possible from each module.
For example, don't be tempted to create a shortcut that exposes an internal implementation detail from a module. You might gain a bit of time in the short term, but you are then likely to incur technical debt many times over as your codebase evolves.
Focus on the unique core of your app so it stands out from other apps.
Don't reinvent the wheel by writing the same boilerplate code again and again. Instead, focus your time and energy on what makes your app unique, and let the Jetpack libraries and other recommended libraries handle the repetitive boilerplate.
Consider how to make each part of your app testable in isolation.
For example, having a well-defined API for fetching data from the network makes it easier to test the module that persists that data in a local database. If instead, you mix the logic from these two modules in one place, or distribute your networking code across your entire code base, it becomes much more difficult—if not impossible—to test effectively.
Types are responsible for their concurrency policy.
If a type is performing long-running blocking work, it should be responsible for moving that computation to the right thread. That particular type knows the type of computation that it is doing and in which thread it should be executed. Types should be main-safe, meaning they're safe to call from the main thread without blocking it.
Persist as much relevant and fresh data as possible.
That way, users can enjoy your app's functionality even when their device is in offline mode. Remember that not all of your users enjoy constant, high-speed connectivity—and even if they do, they can get bad reception in crowded places.
Benefits of Architecture
Having a good Architecture implemented in your app brings a lot of benefits to the project and engineering teams:
- It improves the maintainability, quality and robustness of the overall app.
- It allows the app to scale. More people and more teams can contribute to the same codebase with minimal code conflicts.
- It helps with onboarding. As Architecture brings consistency to your project, new members of the team can quickly get up to speed and be more efficient in less amount of time.
- It is easier to test. A good Architecture encourages simpler types which are generally easier to test.
- Bugs can be investigated methodically with well defined processes.
Investing in Architecture also has a direct impact in your users. They benefit from a more stable application, and more features due to a more productive engineering team. However, Architecture also requires an up-front time investment. To help you justify this time to the rest of your company, take a look at these case studies where other companies share their success stories when having a good architecture in their app.
Samples
The following Google samples demonstrate good app architecture. Go explore them to see this guidance in practice:
Mir 2: Return of the King is a high-quality Legend IP mobile
game authorized by Actoz Soft and developed by HK ZHILI YAOAN LIMITED using the Unity game engine. This game not only perfectly recreates the feelings of Mir 2, a
representative of Korean Wuthering Waves is a high fidelity action RPG game developed by Kuro Games.
Optimizing the power consumption is very important to sustainably deliver a
premium user experience for long gaming sessions. Android Studio introduced the Power Profiler Godot Engine is a popular multiplatform open-source game engine
with robust support for Android. Godot can be used to create games of virtually
any genre and is capable of both 2D and 3D graphics. Godot version 4 introduced
a new rendering system Android Dynamic Performance Framework (ADPF) is a powerful tool from Google for
developers who want to optimize the performance of their applications. Through
its thermal APIs, ADPF provides real-time information about the thermal state
of the NCSoft Lineage W is a massively multiplayer online role-playing game (MMORPG)
developed by NCSoft. This game inherits the legacy of the original Lineage W
game and offers an environment where players from around the world can cooperate
and compete Improving performance and thermal management is essential for developing
successful games on Android. Traditionally, developers had to manage these
issues by decreasing game fidelity or by further optimizing the renderer.
These changes tend to be Call of Duty: Warzone Mobile is a first-person action game in
the popular Call of Duty franchise. The mobile realization of the hugely
popular console and PC game takes advantage of mobile low-level APIs to deliver a
great player experience. From a Summoners War: Chronicles is a mobile MMORPG from South Korean game developer Com2uS, released globally in March 2023. To date, Summoners War has earned over $2.7 billion with more than 180 million downloads worldwide. Set in a fantasy world where Summoners War: Chronicles US(WW) and KR by Com2uS exclusively utilizes Vulkan for rendering on Android, with up to 30%
performance improvements. Vulkan is a modern, cross-platform 3D graphics API
designed to minimize abstraction between device Ares: Rise of Guardians is a mobile-to-PC sci-fi MMORPG developed by Second Dive, a game studio based in
Korea known for its expertise in developing action RPG series. The game is
published by Kakao Games. Set in a vast universe with a detailed, Cat Daddy Games is a wholly-owned 2K studio based in Kirkland, Washington and the developer of NBA 2K Mobile.
The team wanted to improve the overall quality and stability of their games,
specifically by reducing “Application Not Responding” errors Devsisters is a global mobile game developer and publisher, producing casual games based on the Cookie Run IP. Their most popular games include Cookie Run: OvenBreak (running arcade) and Cookie Run: Kingdom (social RPG), which are loved by users NEW STATE Mobile is a battle royale game from Krafton that launched Nov 2021 worldwide, and reached 45M+ downloads in the first month of launch. KRAFTON, Inc. is a collective of independent game development studios brought together to create Based in Poland, Spokko is a group of ambitious creators who are working with a very demanding IP. Although it is part of the CD PROJEKT family, Spokko is an independent company that has transferred the great world of The Witcher: Monster Slayer to Cat Daddy Games is a wholly-owned 2K studio based
in Kirkland, Washington. The teams behind the NBA 2K Mobile, NBA SuperCard, and
WWE SuperCard series were looking for a solution to improve the overall quality
of their games for users, specifically Unreal Engine is a game engine
developed by Epic Games that gives creators across industries freedom
and control to deliver cutting-edge entertainment, compelling
visualizations, and immersive virtual worlds. Some major Android games are
built using Electronic Arts (EA) is a game company headquartered in California, USA. It produces a wide
variety of games across different genres, such as: sports, action, racing,
and simulation. EA's development studio, Firemonkeys, is best known as Based in Warsaw, Poland, game developer CD Projekt RED (CDPR) reimagined their
mini-game in The Witcher 3, GWENT: The Witcher Card Game,
to launch as a standalone free-to-play title on Google Play in March of 2020.
With a large initial file size and In 2000, Gameloft was
created with a passion for games and a desire to bring them to players around
the world. They were an early pioneer developing for mobile and now have a
portfolio of over 190 games. Many of Gameloft's mobile games US-based developer RV AppStudios has over 200 million downloads to date across their portfolio of casual games,
educational kids apps, and utility apps. As an early tester of Google Play Asset
Delivery with their app Puzzle Kids - Animals Shapes and For more than 20 years, Gameloft has created innovative gaming experiences for digital platforms, from mobile games to cross-platform PC and console titles. In addition to its own established franchises, Gameloft develops games for popular brands Pixonic,
a team of video game developers headquartered in Moscow, prides itself on
pursuing every opportunity to upgrade its mobile apps and reach an even wider
audience of gamers. One of the company's best-known titles is War Robots,
a 12-person Gameloft always strives to be among the first developers to publish games on the latest
portable hardware in order to provide gamers with heart-pounding experiences on
the go. That’s why Gameloft knew ChromeOS was the right home for AsphaltMir 2 improves rendering performance by using the Frame Pacing library
Kuro Games reduces 9.68% power consumption through Android Studio Power Profiler and ODPM for Wuthering Waves
Godot Engine Vulkan optimization for Android
Getting started with Android Dynamic Performance Framework (ADPF) in Unreal Engine
NCSoft Lineage W improves sustained performance and prevents thermal throttling by using ADPF
MediaTek enhances dynamic performance of Android SoCs
Call of Duty Warzone Mobile uses Vulkan for better graphics
Com2uS - Google Play Games for PC
Com2uS uses Vulkan for better graphics
Kakao Games increased FPS stability to 96% through Android Adaptability
2K reduces ANR rate by 35% with the Android Game Development Kit
Cookie Run: OvenBreak saves over $200K CDN cost with Play Asset Delivery
NEW STATE Mobile reduces GPU usage by 22% with Android GPU Inspector
The Witcher: Monster Slayer increases reach with Android Performance Tuner
2K delivers higher quality graphics with Play Asset Delivery
"AGDE is freaking awesome!" for Android development with Unreal Engine
Firemonkeys reduced development and debugging time with AGDE
CD Projekt RED reduces update size by 90% and increases update rates by 10% with Play Asset Delivery
Gameloft acquires 10% more new users with Google Play Asset Delivery
RV AppStudios improves user retention with Google Play Asset Delivery
Gameloft reduces device power consumption by 70%, resulting in 35% longer play time with the Game Mode API
Pixonic grew engagement by 25% on ChromeOS by optimizing for large screens
Gameloft races to 9X more revenue by optimizing for ChromeOS
No recommendations at this time.
Try signing in to your Google account.