Xpense Dev Retrospective
Before getting into Xpense's actual code, it's worth knowing what's happening underneath it. Not to sound smart, but because half the weird bugs and design choices later in this series only make sense once you know what Android is doing with your app behind the scenes.
Your phone is a full computer. CPU, RAM, storage, an operating system running all of it. Android is that operating system, and every app, including Xpense, runs on top of it under Android's rules.
Think of Android as a building manager and Xpense as a tenant. The tenant can't break through walls or wander into other tenants' rooms. It can't touch the camera, contacts, or storage on its own, it has to ask the building manager (Android) for permission first. That's the entire reason permission dialogs exist.
Android is stacked in layers, each one depending on the one below it.
The point of this stack: I wrote code at Layer 4 using tools from Layer 3. Layers 1 and 2 handled memory management and pixel rendering automatically. That's what a framework buys you, none of that had to be written by hand.
Kotlin doesn't run directly on a phone. It has to go through a pipeline first, and Xpense's code goes through the exact same one every other Android app does.
| Step | What Happens |
|---|---|
| 1. Source code | Plain .kt files, the code actually written and read |
| 2. Kotlin compiler | Converts it to JVM-compatible bytecode |
| 3. Android toolchain | Converts that bytecode into DEX format, built for devices with limited memory |
| 4. Packaging | DEX files, compiled resources, and the manifest get bundled into an APK |
| 5. ART | Runs the DEX on the actual device. The CPU executes it. The app appears on screen |
Worth knowing: Xpense started out written in Java, not Kotlin. Java goes through this exact same pipeline, just with javac instead of the Kotlin compiler at step 2. Both land on the same DEX format. Android genuinely doesn't care which language produced it, which is part of why the later rewrite to Kotlin didn't require touching how the app fundamentally works.
An APK isn't some special mystery format. It's a ZIP file with a different extension. Rename Xpense's APK to xpense.zip, open it, and there's a normal folder structure inside.
| Inside the APK | What it holds |
|---|---|
classes.dex | All the compiled code, in DEX format |
AndroidManifest.xml | The app's identity and permissions, compiled to binary |
res/ | Compiled layouts, images, colors, strings |
resources.arsc | A table mapping resource IDs to actual resources |
META-INF/ | The digital signature proving the APK came from me |
When Xpense is tapped open, Android doesn't just "start" it. It spins up a new isolated process, hands it its own slice of memory, and runs it there, completely separate from every other app on the phone.
Each app is a locked room in an office building. Xpense can't read another app's data, and another app can't read Xpense's. If Xpense genuinely needs to talk to something outside its room, like opening a link in a browser, it has to go through a formal request, which on Android is called an Intent.
This is also why closing the app doesn't always mean Android kills it right away. It gets parked in memory in case it's reopened, and only gets killed off if the phone actually needs the memory back.
Two kinds of Xpense APK exist. A debug build, made with assembleDebug, signed automatically with a temporary key Android generates, used only for testing on my own device. And a release build, made with assembleRelease, signed with my own personal keystore, which is the actual version that goes out to real users on the Play Store.
Important detail: a debug build and a release build of the same app are treated as two completely different apps if their signatures don't match. You can't install a release APK as an "update" over a debug install. Signing gets its own full post later in this series, since losing a release keystore is one of the few genuinely unrecoverable mistakes in Android development.
That's the foundation. Next up, actually opening Xpense's project folder and going through what every file and directory in it is for, and why it's organized the way it is.