Skip to main content
An Android app is a signed package — an APK, or an app bundle that Google Play turns into APKs — holding a manifest that tells the system where the app starts, compiled Kotlin code whose screens are functions that turn state into UI, and the resources the app draws on. This page walks that anatomy for readers who build software but haven’t built for Android: what is in the package, where code starts running, how a screen is drawn, where the logic lives, and how the finished app reaches users. Each section links the page of Google’s developer documentation that treats it in full.
Block diagram of a modern Android app: AndroidManifest.xml declares MainActivity, which sets the Jetpack Compose composable tree as its content; state flows from a state holder to the composables and events flow back; services and background workers handle work that outlives the screen

The chain inside every modern Android app: the manifest declares the entry activity, the activity sets a composable tree as its content, state flows in from a state holder, and long-running work is handed off to services.

What is inside an Android app?

The artifact users install is an APK — a signed archive of compiled code, resources (icons, strings, themes), and native libraries. What developers upload to Google Play is usually an app bundle (.aab), from which Play generates a smaller APK tailored to each device’s screen density, CPU architecture, and language. At the root of the package sits AndroidManifest.xml. It names the app, lists every entry point the system may launch, and declares the permissions the app requests — camera, location, notifications. Android reads the manifest before running any of the app’s code, so nothing undeclared can be launched or granted.

Where does an Android app start? The entry points

There is no single main() function to look for. The system starts an app at whichever declared entry point the situation calls for, and the manifest lists them all. An Activity is the entry point for anything the user sees. When someone taps the app’s icon, Android launches the activity the manifest marks as the launcher — conventionally named MainActivity — and hands it a window to fill. A modern app typically has one activity hosting every screen, switching between them in code. The Application class is instantiated before any activity, once per process. Apps use it for initialization that everything else depends on — logging, dependency wiring, crash reporting. Broadcast receivers are entry points with no UI at all: the system invokes them when an event they registered for occurs — the device finished booting, connectivity changed, an alarm fired — even if the app isn’t running.

How is a screen drawn? Jetpack Compose

Screens are written in Jetpack Compose, Google’s recommended toolkit for native UI: a screen is a Kotlin function annotated @Composable that describes what to show for the current state. When the state changes, the framework re-runs the affected functions and updates only what differs — the developer never mutates the screen by hand. A minimal but complete app:
(Imports omitted.) The activity’s one job here is setContent — everything visible is the composable tree. Search results will also surface the older layer: XML layout files inflated into a tree of View objects, edited through code. That system still runs in production apps and in most tutorials written before Compose, which is why both appear when you search — but a new project’s screens are Compose functions.

Where does the app’s logic live?

Logic lives in a state holder: a class that owns a screen’s state, exposes it as an observable value, and updates it in response to events from the UI — the pattern Google’s app architecture guide recommends, with Jetpack’s ViewModel as the common implementation. State flows one way, from the holder down into the composable tree; events flow back up as function calls. The payoff is that a screen’s behavior can be exercised in a plain unit test — construct the state holder, send it events, assert on the state — with no device attached.

What runs in the background?

Work that must outlive the screen — uploads, sync, media playback — runs through the platform’s background-work facilities: scheduled workers for deferrable jobs, and foreground services for work the user is actively aware of, which must show a notification. Android decides when deferred work actually runs, batching it around battery and network conditions, so apps request work rather than simply spawning threads that the system may kill.

How is an Android app built? Gradle

Every Android app is built by Gradle, driven by build files checked into the project — there is no IDE-owned project file that the build depends on. The build.gradle.kts of the app module declares the app’s identity and its platform window:
Three numbers define that window: compileSdk is the API the code compiles against, minSdk is the oldest Android version the app installs on, and targetSdk declares which version’s behavior rules the app has been tested for. What each API level corresponds to, and how many devices each minSdk choice reaches, is the whole subject of Android API levels: versions, codenames, and device coverage.

Where do you run it? Devices and emulators

Development runs on a real device over USB or on the Android Emulator — a virtual device booted from a system image of a chosen API level, defined by an AVD (“Android Virtual Device”) profile such as a Pixel 8 running API 36. The emulator runs the same system the phone does, so the app, its background workers, and its notifications behave as on hardware. On a Mac, one setup pass installs the SDK, a JDK, and a first emulator — Set up the Android toolchain documents it.
Android emulator screen showing the Compose counter app: a status bar, the text Taps: 7, and a Tap me button

The counter app from this page, built with Gradle and installed on a Pixel 8 emulator running API level 36.

How does an app reach users?

Through Google Play: the developer uploads the app bundle, and Play verifies, signs, and distributes per-device APKs. With Play App Signing, Google holds the signing key and every update must come from the same developer account — devices refuse an update whose signature doesn’t match the installed app. Play also enforces a floor on targetSdk for new submissions and updates, so shipped apps track recent platform behavior.

Common questions

An APK is what a device installs and runs. An app bundle (.aab) is what a developer uploads to Google Play: a superset package from which Play builds a smaller APK per device configuration. Outside Play — a local build, a direct download — you deal in APKs.
Not anymore, as a rule. An Activity is the entry point that owns a window; a modern app has one activity, and its screens are composable functions the app navigates between inside that window. Apps built earlier often used one activity per screen, and both designs run fine today.
Only to read existing code and older tutorials. New screens are written in Jetpack Compose — Kotlin functions, no XML. The XML View system remains supported, and large apps commonly contain both while newer screens accumulate in Compose.
Kotlin. Android’s own APIs, documentation, and tooling are Kotlin-first, and Compose is Kotlin-only. Java remains fully supported for existing code, so long-lived apps often carry both languages.
Yes — the SDK, the emulator, and the JDK all run natively on macOS, alongside Xcode. Set up the Android toolchain walks through the one-banner install Modaal provides.
Modaal builds the apps this page describes without you assembling the toolchain yourself; Set up the Android toolchain covers what it installs.

Sources and further reading

Android API levels and device coverage

Every Android version with its API level and codename, live device-coverage numbers, and how to choose a minSdk.

The Duet framework

iOS and Android from one shared core: feature logic written once, native SwiftUI and Jetpack Compose interfaces, and a CI gate that keeps the two apps in agreement.

Kotlin Multiplatform Android app structure

This page’s anatomy, applied: what a Duet-built Android app puts in each module, shown on a shipped app.

How to build testable Android apps

What a fixed architecture buys over a blank project: pure reducers, effects as data, and recorded behavior tests.
Last modified on September 3, 2026