๐ŸŒฑ Android Development for Beginners

How an Android App Works Internally

When a user taps your icon, a lot happens before your first line of code runs, and a lot keeps happening while it does. Understanding processes, the main thread, ART and process death explains many bugs that otherwise look random.

โœ๏ธ Yunok Tech ๐Ÿ“… Updated โฑ 5 min read ๐ŸŽฏ Beginner
On this page
  1. 1. What's inside an installed app
  2. 2. From tap to first frame
  3. 3. ART: how your code actually executes
  4. 4. The main thread and its message loop
  5. 5. The sandbox and Binder IPC
  6. 6. Lifecycles: who's in control
  7. 7. Process death: the bug that only appears later
  8. How this knowledge prevents real bugs
  9. Frequently asked questions
  10. Conclusion

Why does a form lose its text when the user returns after an hour? Why does a long loop freeze the screen? Why can't your app read another app's files? These aren't Android quirks. They follow directly from how Android runs apps.

This article walks through what happens from install to tap to background, in plain language, with code showing how each internal mechanism shapes the way you write apps.

1. What's inside an installed app

You publish an Android App Bundle. Google Play turns it into optimised APKs for each device. An installed APK contains:

  • classes.dex: your Kotlin and library code compiled to DEX bytecode.
  • resources.arsc and res/: compiled resources (layouts, strings, drawables).
  • AndroidManifest.xml: the app's identity, components, permissions and requirements.
  • lib/: native libraries, if any.
  • A signature proving who built it.

At install time, the system assigns the app a unique Linux user ID and a private data directory. That is the start of the sandbox.

2. From tap to first frame

Cold start sequence of an Android app The launcher asks system_server's ActivityManager to start the app. Zygote forks a new process with preloaded framework classes. The app's Application object is created, then the launcher Activity runs onCreate, its UI is measured and drawn, and the first frame appears. Tap iconlauncher system_serverActivityManager Zygoteforks process ApplicationonCreate() Activity.onCreate()โ†’ measure, draw, first frame Everything in Application.onCreate() delays the first frame. Keep it small: that's the biggest lever on cold start time.
A cold start. Zygote is a pre-warmed process that already has framework classes loaded, so forking it is much faster than starting a JVM from scratch.

Not every launch is a cold start. If your process is still alive, Android may perform a warm or hot start, reusing the existing process and possibly the existing Activity. See Reducing Android App Startup Time.

3. ART: how your code actually executes

The Android Runtime (ART) executes DEX bytecode. Modern ART combines an interpreter, just-in-time (JIT) compilation of hot methods, and ahead-of-time (AOT) compilation guided by profiles, including Baseline Profiles you ship with your app. ART also manages memory with a garbage collector, which is why creating many short-lived objects in a tight loop, such as inside a composable that recomposes often, can cause jank.

4. The main thread and its message loop

Each app process has one main thread, also called the UI thread, running a Looper that processes a queue of messages: input events, lifecycle callbacks, drawing frames, and your posted work. Everything runs one item at a time.

Why blocking the main thread freezes the UIKotlin
button.setOnClickListener {
    // โŒ Runs on the main thread. While this sleeps, no frames are drawn and no touches are handled.
    Thread.sleep(6_000)          // long enough to trigger an ANR dialog on input
}

// โœ… Move slow work off the main thread; resume on main to update UI.
button.setOnClickListener {
    lifecycleScope.launch {
        val report = withContext(Dispatchers.Default) { buildLargeReport() }
        textView.text = report.summary
    }
}

If the main thread can't respond to input for about five seconds, Android shows an ANR ("Application Not Responding") dialog. See ANR Errors Explained.

5. The sandbox and Binder IPC

Because each app runs as its own Linux user in its own process, it can't read other apps' private files or memory. To use system features such as location, notifications or the camera, your app calls system services in system_server through Binder, Android's inter-process communication mechanism, subject to permissions. When you call getSystemService(), you get a proxy that sends Binder transactions to the real service.

6. Lifecycles: who's in control

You don't control when your Activity is created or destroyed; the system does. It calls lifecycle methods (onCreate, onStart, onResume, onPause, onStop, onDestroy) as the user navigates, rotates the screen or switches apps. Configuration changes such as rotation, language or dark mode recreate the Activity by default. That's why state belongs in a ViewModel, which survives configuration changes.

7. Process death: the bug that only appears later

When memory is low, Android may kill your process while it's in the background. When the user returns, Android recreates the Activity in a new process, and it expects your app to restore what the user saw. ViewModels don't survive process death. Saved instance state does.

Surviving process death in a ViewModelKotlin
class CheckoutViewModel(private val savedState: SavedStateHandle) : ViewModel() {
    // Persisted in the saved-instance-state Bundle: survives rotation AND process death.
    val couponCode: StateFlow<String> = savedState.getStateFlow("coupon", "")

    fun onCouponChange(code: String) { savedState["coupon"] = code }
}

// In Compose, rememberSaveable does the same for UI-element state:
// var query by rememberSaveable { mutableStateOf("") }
Test it

Enable Don't keep activities in Developer options, or put the app in the background and run adb shell am kill <package>, then return to the app. Anything you lose is a process-death bug.

How this knowledge prevents real bugs

"Form clears randomly"

Cause: process death. Fix: store user input in SavedStateHandle or rememberSaveable, and drafts in Room or DataStore.

"App freezes when exporting"

Cause: file I/O on the main thread. Fix: withContext(Dispatchers.IO) and a progress state.

"Slow startup after adding SDKs"

Cause: heavy Application.onCreate(). Fix: lazy initialisation after the first frame.

Frequently asked questions

Does each app run in its own process?

By default, yes: one process per app, running as a unique Linux user. Apps can declare additional processes for specific components, but most apps don't need to.

Is ART a Java Virtual Machine?

No. ART runs DEX bytecode, not Java class files, and it uses its own compilation and garbage-collection strategies. The build converts JVM bytecode to DEX.

Why is Application.onCreate() called for background work too?

Starting any component (an Activity, Service, BroadcastReceiver, WorkManager job or ContentProvider) in a new process creates the Application first. Heavy initialisation there slows every entry point.

Will my coroutines keep running after process death?

No. The process and all its threads are gone. Use WorkManager for work that must complete reliably.

Conclusion

An Android app is a sandboxed process forked from Zygote, executing DEX on ART, driven by a single main-thread message loop, with lifecycles controlled by the system and the ever-present possibility of process death. Writing apps that respect these facts is most of what "Android best practices" means.

Next step: learn the four building blocks the system can launch in Android App Components.

Official references

๐Ÿ“ฑ Apps built by Yunok Tech

Real Android apps on Google Play, free to download.

See all our apps โ†’

Ready to build real Android applications?

Reading a roadmap is the easy part. The real progress happens when you build a foundation step by step, with someone reviewing your code and answering the questions that come up along the way.

Move beyond theory and strengthen your Android development skills with Yunoktech's Android course. Learn core concepts, practical implementation, modern Android development techniques, and the reasoning behind building maintainable applications.

Explore the Android course and take your next step as an Android developer.

Share: LinkedIn X / Twitter WhatsApp