Android architecture — the big picture
Start with one simple idea
Your Android application does not control the whole device. It runs as one restricted Linux process and asks Android to perform privileged work on its behalf.
For example, this looks like an ordinary Kotlin call:
notificationManager.notify(ORDER_READY_ID, notification)
But your app cannot directly place an item in the system notification shade. That UI belongs to Android. The call goes through framework code, crosses a process boundary, reaches a system service, passes validation and permission checks, and only then changes system-managed state.
That is the reason Android architecture matters to an application engineer: a line that looks local may involve other processes, serialization, security checks, scheduling, hardware, and failure conditions.
:::warning Android-only example Android framework code cannot run in Kotlin Playground. Each Android-only example is therefore labelled rather than linked to a broken playground. :::
The layers, in useful rather than academic language
┌─────────────────────────────────────────────────────────────┐
│ Your application │
│ CheckoutActivity, ViewModel, repository, Compose UI │
└──────────────────────────────┬──────────────────────────────┘
│ calls Android APIs
┌──────────────────────────────▼──────────────────────────────┐
│ Application framework │
│ ActivityManager, NotificationManager, LocationManager │
└──────────────────────────────┬──────────────────────────────┘
│ commonly Binder IPC
┌──────────────────────────────▼──────────────────────────────┐
│ System services and native services │
│ activity/task, package, window, notification, camera │
└──────────────────────────────┬──────────────────────────────┘
│ HALs, native APIs, syscalls
┌──────────────────────────────▼──────────────────────────────┐
│ Linux kernel and device drivers │
│ processes, memory, scheduling, networking, camera driver │
└─────────────────────────────────────────────────────────────┘
This is a responsibility map, not a promise that every operation travels through every box. Reading an in-memory string stays inside your process. Asking the camera service for a camera crosses processes and eventually reaches hardware-facing code.
What each layer contributes
| Layer | Responsibility | What an app developer sees |
|---|---|---|
| App | Product behavior and UI | Kotlin/Java classes, resources, native libraries |
| Framework | Stable, high-level Android API | Activity, Context, managers, Views, Compose integration |
| System services | Central coordination and policy | Usually hidden behind a manager or Binder proxy |
| ART and native runtime | Executes app bytecode/native code | GC, compilation, threads, JNI behavior |
| HAL | Standard contract around device-specific hardware | Usually indirect access through framework APIs |
| Kernel | Isolation and low-level resource control | Processes, UIDs, memory, scheduling, drivers |
Android’s official architecture documentation explains that framework APIs communicate with system services and that hardware abstraction layers isolate higher layers from vendor implementations. See the AOSP architecture overview.
Scenario: opening an order-details screen
val intent = Intent(this, OrderDetailsActivity::class.java)
.putExtra("orderId", "ORD-42")
startActivity(intent)
What actually happens
- Your Activity calls a framework API.
- Framework code packages the request for Android’s activity/task manager.
- Binder transports the request to the system process.
- Android resolves the target against installed package and manifest information.
- Android checks whether the caller may launch it and applies task/back-stack rules.
- If the destination process does not exist, Android starts one.
- The app-side framework creates
OrderDetailsActivity. - Android calls lifecycle methods such as
onCreate(). - Window management coordinates the screen’s surface.
The important correction is this: startActivity() does not behave like OrderDetailsActivity() in ordinary Kotlin. Android owns component creation and lifecycle delivery.
Scenario: why the manifest matters
<application ...>
<activity
android:name=".OrderDetailsActivity"
android:exported="false" />
</application>
The class file alone is insufficient. Android needs installed metadata describing components, permissions, intent filters, and device requirements. For app components, this metadata comes from the merged manifest.
exported="false" means other applications cannot directly launch this Activity. Your own app still can.
Local call or remote call?
val text = order.title.uppercase() // Local computation
val info = packageManager.getApplicationInfo( // May involve system service IPC
packageName,
0
)
Do not memorize which internal implementation is remote in every Android release. Use the safer architectural rule: framework APIs that coordinate global device state may cross a process boundary, so avoid needless repetition and never rely on implementation accidents.
✕ Avoid repeated system queries in a hot loop
orders.forEach {
val appInfo = packageManager.getApplicationInfo(packageName, 0)
render(it, appInfo)
}
✓ Prefer obtaining stable information once
val appInfo = packageManager.getApplicationInfo(packageName, 0)
orders.forEach { order -> render(order, appInfo) }
The improvement is not merely “fewer function calls.” It avoids repeating work that can include synchronization, IPC, lookup, and allocation.
Architecture does not mean app architecture
These terms are related but different:
| Term | Question it answers |
|---|---|
| Android platform architecture | How does Android execute, isolate, and coordinate apps and hardware? |
| Application architecture | How should this app organize UI, domain logic, and data? |
| System design | How should the complete mobile/backend system satisfy scale, reliability, and security needs? |
MVVM or Clean Architecture does not replace knowledge of process death, Binder, or lifecycle. It organizes your code inside the constraints created by the platform.
What you should remember before continuing
- Your app is a guest in an OS-managed process.
- Android components are system-controlled entry points; there is no app-owned
main()lifecycle. - Framework managers can be client-side façades over services in another process.
- Binder is the main IPC mechanism connecting those processes.
- The kernel supplies isolation, processes, memory, scheduling, and drivers.
- The HAL hides vendor-specific hardware details behind standard interfaces.
Android is a layered Linux-based platform. Applications use framework APIs; many framework operations communicate through Binder with system services; native services and HALs connect the framework to device-specific functionality; and the Linux kernel provides process isolation, memory management, scheduling, networking, and drivers. This matters because an innocent-looking API call can cross processes and therefore has lifecycle, performance, security, and failure implications.
Check your understanding
- Why does
startActivity()not directly construct an Activity? - Which layer decides whether your app has camera permission?
- Why can repeated calls to a framework manager be more expensive than repeated calls to your own object?
- Explain the difference between Android platform architecture and MVVM.
- Trace the layers used when an app requests the current location.