Zygote
Components & Intentsbeginner

The four components

An Android app has no main(). It has four kinds of entry point that the system can start on its own — and that fact explains most of the platform's design.

11 minAPI 1–37 · 17interactivequizverified 2026-10-27

Most programs begin at a known place. A C program starts at main, a web server at a listen call, a script at its first line. An Android app never begins at all — it is entered, from the outside, at a point the operating system chooses.

TL;DR#

  • An Android app has no user-written entry point. The framework's ActivityThread calls main() in a forked process, and from then on the system invokes your code through components.
  • There are four component types: (a screen), (work with no UI), (a short reaction to a message), and (data behind a URI).
  • Every component is declared in the manifest, and the system — not your code — decides when to create it, which process it runs in, and whether another app may reach it.
  • A component's lifetime is not the process's lifetime. The system can create a component in a running process, or kill the whole process once no component matters.
  • Components are the boundary between apps. An Intent names an action; the system finds a component that can perform it, possibly in an app you have never met.

Mental model#

Think of a large office building with four kinds of room.

A meeting room has a door, a table and a schedule. Someone books it, people come in, and when the meeting ends the room is empty again but the building stays. That is an Activity.

A back office has no visitor entrance. Work arrives through an internal hatch, gets done, and the room stays open as long as there is work. That is a Service.

A mail slot in the lobby accepts one envelope at a time, drops it on a desk, and is done. Nobody waits at the slot. That is a BroadcastReceiver.

A records counter has a clerk behind glass who will fetch a document by its reference number, check your badge, and hand it over. That is a ContentProvider.

Where the analogy breaks#

The building has a landlord who can demolish any room at any moment — and the room's occupant is not consulted. Android's system can destroy your Activity while it is in the background, kill your process to reclaim memory, and later rebuild the screen from a saved bundle. The office analogy suggests stable rooms; Android offers restartable ones. That single difference is why so much of this module is about lifecycle and saved state rather than about drawing screens.

The big picture#

Loading diagram…
Text description of this diagram

A sequence diagram of starting an activity. Your app calls startActivity, which crosses Binder into ActivityTaskManagerService inside system_server. ActivityTaskManager asks PackageManagerService to resolve the intent against installed intent filters, and gets back a ResolveInfo. It checks that the target component is exported, that the caller holds any declared permission, and it pauses the current activity. If the target app has no live process, ActivityManager asks Zygote to fork one; the child inherits preloaded classes copy-on-write and drops to the app's UID. The new process calls bindApplication, creating the Application object, and only then does ActivityThread instantiate the Activity and run onCreate, onStart and onResume.

Read that diagram once and note how much happens before your onCreate. The activity is the last thing created, not the first. Everything above it — resolution, permission checks, process creation, application setup — happens because the system, not your code, is driving.

Deep dive#

An app is a set of entry points, not a program#

The manifest is the app's real interface description. It declares the package, the components, the permissions the app wants and the permissions it offers, and the minimum and target SDK levels.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.notes">
 
    <application android:label="Notes" android:theme="@style/Theme.Notes">
 
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <service
            android:name=".SyncService"
            android:exported="false" />
 
        <receiver
            android:name=".BootReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
 
        <provider
            android:name=".NotesProvider"
            android:authorities="com.example.notes.provider"
            android:exported="false" />
 
    </application>
</manifest>

That file is not configuration in the usual sense. It is a published contract: the launcher reads it to know what icon to show, the package manager reads it to answer "who can open this?", and the system reads it to decide which process to start. Since Android 12 (API 31) every component with an intent filter must state android:exported explicitly, because the old default silently changed between releases.1 API 31Android 12

Activity: a screen with a lifecycle#

An Activity owns a window and a state machine. It is the only component users see, and the only one whose destruction is routine rather than exceptional: rotate the phone and the activity is destroyed and recreated.2

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NotesTheme {
                NotesApp()
            }
        }
    }
}

Modern apps increasingly use a single activity and put screens inside it, because Compose destinations do not need a window each. The activity is still there; it is just no longer the unit of navigation.

Service: work without a window#

A Service is a component the system can start, bind to, or stop, and which keeps running when no UI is visible. It is emphatically not a thread. onStartCommand and onBind run on the main thread unless you move them. Confusing a service with concurrency is one of the classic Android mistakes: a service that blocks its main thread produces an ANR just like any other component.3

Since Android 8.0 the platform has tightened when a background service may keep running, which is why most real work now happens in WorkManager or in a foreground service with a visible notification.4

BroadcastReceiver: one callback, then gone#

A receiver exists for the duration of a single onReceive call. The system may create the process for it, deliver the intent, and let the process die again. The callback has a deadline — roughly ten seconds for a foreground broadcast, sixty for a background one — and exceeding it is an ANR.5

class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action != Intent.ACTION_BOOT_COMPLETED) return
        // Enqueue work; do not do it here.
        WorkManager.getInstance(context).enqueue(SyncWorker.request())
    }
}

The pattern in that snippet is the whole lesson of broadcast receivers: hand off, do not work. Since Android 8.0 most implicit broadcasts cannot be declared in the manifest at all, precisely because waking every app for every system event did not scale.5

ContentProvider: data behind a URI#

A ContentProvider exposes structured data — rows, files, streams — through a URI interface, with permissions enforced by the framework rather than by your code. ContentResolver is the client side; you almost never talk to a provider object directly.6

val resolver = context.contentResolver
resolver.query(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME),
    null, null, null,
)?.use { cursor ->
    val nameCol = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
    while (cursor.moveToNext()) {
        println(cursor.getString(nameCol))
    }
}

Providers have one more property that surprises people: the provider object is created before the Application object, because other components in the process may need data during application startup. That ordering is deliberate and is why provider onCreate must be fast.6

What "component" buys you#

The four types are not an arbitrary list. Each one answers a different question the system needs to ask:

ComponentThe system's questionLifetime
Activity"What is the user looking at?"Until it is finished or the system reclaims it
Service"What should keep running without a screen?"Until stopped, or the process is killed
BroadcastReceiver"Who should hear about this event?"One onReceive call
ContentProvider"Who can read or write this data?"As long as the process lives

Because the system asks these questions, it can make decisions no app could make alone: start your app in the background to deliver a message, keep a music player alive while the screen is off, or refuse to let a stranger read your database.

Export, permission, and the trust boundary#

A component is a door. By default, whether that door is open to other apps depends on the component type: activities and services with intent filters are reachable, providers are not. android:exported makes the choice explicit, and an android:permission on the component turns the door into one with a lock.

<activity
    android:name=".ShareTargetActivity"
    android:exported="true"
    android:permission="com.example.notes.permission.SHARE" />

The check happens in system_server, not in your process. Your code never gets to decide whether the caller was allowed, which is the point: a compromised app cannot talk its way past a check it does not run.

How it evolved#

The four component types have existed since Android 1.0. What changed is how tightly the platform controls when they may run.

Background and power changes12 releases
APIVersionWhat changed
11.0
  • The original android.* SDK: Activity, Service, BroadcastReceiver, ContentProvider
174.2 Jelly Bean
  • Daydream service
194.4 KitKat
  • AlarmManager becomes inexact by default for targeting apps
215.0 Lollipop
  • JobScheduler
  • Implicit service intents restricted for targeting apps
268.0 Oreo
  • Background service limits
289 Pie
  • Foreground service permission
2910
  • Background activity starts restricted
3011
  • Background location requires a second request
3112
  • Exact alarms need SCHEDULE_EXACT_ALARM
  • Foreground service launch restrictions from the background
3414 Upside Down Cake
  • Foreground service types required when targeting 34
  • User-initiated data transfer jobs
3515 Vanilla Ice Cream
  • Deprecated APIs: WindowInsetsController hiding, elevate the importance of foreground services
3616 Baklava
  • JobScheduler quota changes

Three eras are visible in that table. In the first, any component could start anything and background work was unlimited. In the second, starting with Android 8.0, background services and implicit broadcasts were restricted. In the third, starting with Android 12, even starting an activity from the background requires an exemption. Each restriction pushed work toward WorkManager, foreground services and notification-driven flows — components that the user can see.

Under the hood#

The four declarations map onto four framework base classes, and each has a server-side counterpart that decides when to instantiate it.

Curiosity corner#

  • The launcher is just an app with a category. CATEGORY_LAUNCHER on a MAIN activity is what puts an icon on the home screen. Replace the launcher and the icons change with it.
  • A component can live in another process. android:process=":sync" in the manifest puts a component in its own process, with its own memory and its own main thread.
  • ContentProvider is older than the concept of an app store. It shipped in Android 1.0 as the way system apps shared contacts and call logs.

Try it#

List an app's components

adb shell dumpsys package com.example.notes | grep -A 40 "Resolver Table"

Example output
Activity Resolver Table:
Non-Data Actions:
    android.intent.action.MAIN:
      .../.MainActivity filter ...

Service Resolver Table:
...
Receiver Resolver Table:
...
Provider Resolver Table:
...
Start an activity from the shell

adb shell am start -n com.example.notes/.MainActivity

Check yourself#

Check yourself

1 / 12

You call `startActivity()` for an activity in another app. Which check does the system perform before delivering the intent?

0 answered · 0 correct

Sources#

Sources

Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.

  1. docsApp manifest overviewdeveloper.android.comaccessed 2026-10-27↩
  2. docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩
  3. docsServices overview | Background workdeveloper.android.comaccessed 2026-10-27↩
  4. docsProcesses and threads overview | App qualitydeveloper.android.comaccessed 2026-10-27↩
  5. docsBroadcasts overview | Background workdeveloper.android.comaccessed 2026-10-27↩
  6. docsContent provider | App data and filesdeveloper.android.comaccessed 2026-10-27↩
  7. docsComponentsdeveloper.android.comaccessed 2026-10-27↩
  8. docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩