Why the first build takes so long
Your first build took minutes and your second took seconds, and nothing was broken. Here is exactly where those minutes went, why later builds skip almost all of it, and why a small dependency list matters far more on a phone than on a laptop.
Nothing is wrong with your phone
In Lesson 0.3 you pressed Run for the first time and waited. Possibly for several minutes. The phone got warm. You may reasonably have wondered whether it had hung.
Then you changed one line, pressed Run again, and it finished in a few seconds.
Both of those are correct behaviour, and the gap between them is not a mystery. A first build does four expensive things that a second build does almost none of. Once you know which four, you also know how to keep every build after the first one fast — which, on a device with no fan, is a skill worth having.
Think about cooking your first meal in a brand new flat.
The kitchen is empty. Before you can start you need a pan, a wooden spoon, salt, oil, and a sharp knife. So the first meal takes an hour and a half, and most of that is a trip to the shops and putting things away in cupboards you have never opened.
The second meal takes twenty minutes. Same recipe, same cook. The difference is that the pan is already in the cupboard, the oil is already on the shelf, and you already know where the sharp knife lives.
Nothing got faster. The shopping just already happened.
Where the first minutes actually go
1. Downloading Gradle itself
Your project does not contain . It contains the : a small script and a file naming an exact version. The first time you build, that script downloads the real thing — the Gradle 8.11.1 distribution, which is a zip of roughly 130 MB — and unpacks it.
This happens once per Gradle version, per device. Every project you ever build with Gradle 8.11.1 reuses it. That is one of the reasons this course pins a single version everywhere.
2. Downloading every library
Then Gradle works through the shopping list. Not just your eight lines of — every one of their dependencies too. Dice Duel resolves to about forty modules: the Compose libraries, AndroidX core, lifecycle, activity, coroutines.
That is hundreds of individual files, each fetched over the network. Add the and the Kotlin compiler, which are themselves substantial downloads.
These land in a shared cache on your device and are never downloaded again — not for this project, and not for the next one either.
3. Unpacking and transforming them
Android libraries do not arrive in the shape the build needs. Each one has to be unpacked, its resources extracted, its code converted. Gradle keeps the results in a transforms folder next to the downloads, and this step alone can take longer than the compile on a first build.
Also cached. Also never repeated for the same library version.
4. Compiling everything, cold
Finally the actual work, with nothing to reuse:
- The Kotlin compiler starts up and compiles every file in your project.
- compiles every resource and builds the lookup table.
- The merges every library's manifest.
- turns all of that into .
Four sizeable programs, each starting from nothing, each doing the maximum amount of work it will ever do.
Focus Flow — the largest app in this course, with Room, DataStore, navigation and notifications — was built from a clean import on a physical Android phone in Pocket Studio.
The first build took 270.8 seconds, about four and a half minutes, and produced a 19.58 MB debug with zero issues. That is a normal, healthy first build, not a fault.
Your own number will differ. A faster phone, a warmer cache or a smaller app will all pull it down. What matters is the shape: minutes the first time, seconds afterwards.
That screen reports 4s and 13.585s for the same rebuild, and both are honest.
BUILD SUCCESSFUL in 4s is Gradle timing its own work. Build succeeded in 13.585s is Pocket Studio timing the whole button press — starting Gradle, talking to the daemon, and collecting the result afterwards.
The gap between them is fixed overhead. It barely moves whether the build is tiny or huge, which is why it looks enormous next to a 4-second rebuild and invisible next to a 270-second one.
Why the second build is so much faster
Four different mechanisms, all working at once.
The downloads are already there. Steps 1, 2 and 3 above simply do not happen. On a second build they cost zero.
Gradle checks what actually changed. Every declares its inputs and its outputs. Before running one, Gradle fingerprints the inputs and compares them with last time. If nothing changed, the task is skipped and the previous output is reused. That is the , and you can watch it working in the last line of every build:
41 actionable tasks: 3 executed, 38 up-to-dateThirty-eight of the forty-one steps were skipped entirely.
The compiler only recompiles what it must. Change one function in one file and the Kotlin compiler works out which files could possibly be affected and recompiles only those. This is an .
The daemon is already warm. The from your last build is still running: the has started, the classes are loaded, and the code inside it has been optimised by running. A cold JVM start alone can cost several seconds.
Real output from a rebuild of Focus Flow in Pocket Studio. Forty-nine of fifty steps did no work at all.
Why this matters double on a phone
Every word above is true on a laptop too. Here is what is different in your hand.
There is no fan. A phone that gets hot protects itself by slowing its own processor down. This is called thermal throttling, and it means a long build gets slower as it goes. The last minute of a ten-minute build can be running at half the speed of the first. A laptop with a fan simply does not have this problem.
There is a battery. A full cold build is one of the heaviest things you can ask a phone to do. Plug in before a big one.
There is less memory. The Gradle daemon is configured to use up to 2 GB:
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8If the phone runs short, Android will kill the daemon mid-build — and you get an error that looks alarming but simply means "out of memory". It is in the Error Doctor below.
There is less storage. The Gradle distribution, the downloaded libraries and the transformed copies add up to well over a gigabyte before your app contributes anything.
The practical conclusion: keep the list short
Everything above scales with how many libraries you use.
Each extra means more to download, more to unpack, more to merge, more to dex, and a bigger for Gradle to reason about on every single build. A library that also generates code — anything using — adds a whole compile step of its own.
This is not theory. It is why the three apps in this course are built the way they are:
| Choice | The easy way | What this course does | |
|---|---|---|---|
| Dice faces | An image library and six PNGs | Drawn with Compose [[canvas | Canvas]] |
| Focus Flow's stats chart | A charting library | Drawn with Compose Canvas | |
| Wiring objects together | A dependency-injection library | Plain Kotlin constructors | |
| Loading images | An image-loading library | Not needed — no remote images |
Dice Duel declares eight dependencies. Adding a charting library and a dependency-injection framework would roughly double the graph, add a code-generation step, and make every build on your phone slower and hotter for the rest of the course.
You can always add a library later, once you can feel what it costs. Starting without one is free.
Four things not to do
- Do not run Clean.
./gradlew cleandeletes exactly the outputs that let Gradle skip thirty-eight steps. It is a fix for almost nothing and it guarantees a slow next build. Try it only when the build output is clearly nonsense. - Do not change Gradle versions casually. A new version means a fresh 130 MB download and a fresh cache of transformed libraries.
- Do not kill the daemon between builds. Leaving it alive is the point of it.
- Do not build with fifteen other apps open. Free the memory first; you will get it back in build time.
- Open Pocket Studio, tap Projects, open your Part 0 app.
- Tap Build and press Run. Note the time on the
BUILD SUCCESSFUL in …line and theactionable tasksline under it. - Press Run again without changing anything. Almost every task should now say
UP-TO-DATE, and the build should finish in a second or two. - Tap Editor, change one piece of visible text, and press Run again. Compare the
actionable tasksline: a handful executed, the rest up to date. - Open the Terminal in the Build tab and run
./gradlew :app:assembleDebug --profile. When it finishes it prints the path to an HTML report. Open it from the file tree to see exactly which tasks took the time. - Only if you are curious, and while plugged in: run
./gradlew cleanand then build again. That is roughly what a first build feels like. Now you know never to do it casually.
BUILD SUCCESSFUL and by whether an APK appeared, not by the absence of warnings — a healthy Android build is often noisy.gradle.properties — try org.gradle.jvmargs=-Xmx1536m — so Android is less tempted to kill it.gradle.properties — org.gradle.jvmargs=-Xmx3072m — but only if the phone actually has the RAM spare. On a 4 GB phone, closing other apps is the better move../gradlew --stop in the Terminal to shut every daemon down and start fresh.build folder inside your project reclaims a little, at the cost of a slower next build.- A first build pays four one-time costs: downloading itself, downloading every , unpacking and transforming them, and compiling everything cold.
- Later builds skip nearly all of it: the reuses unchanged task outputs, compilation is , and the is already warm.
41 actionable tasks: 3 executed, 38 up-to-dateis the line that tells you it is working.- On a phone this matters double: no fan means thermal throttling, and memory and storage are tighter. A smaller builds faster and cooler.
- This course's apps deliberately use very few dependencies — the dice faces and the stats chart are drawn on precisely so no library is needed.
- Avoid
clean. It throws away the very thing that makes builds fast. - Next: the three SDK numbers in
build.gradle.kts— what each one really controls, and why mixing them up is the most common misunderstanding in Android.