Techbase Academy
Android · Lesson 03

Create Your First Android App

12 phasesProject Anatomy ReportAndroid Studio
Phase 1 of 12
Lesson Introduction

Welcome back! In lesson 02 you installed Android Studio and took a tour of the IDE. Today you stop being a visitor and become a builder: you will create your very first Android app, open every folder the wizard generates, read the important files line by line, and learn what Gradle actually does when you press the green button.

Many beginners treat a new project like a locked room: "Android Studio made a hundred files, I will touch only the one I am told to touch." That works for a day and then becomes painful, because the first time something breaks (a red error in build.gradle.kts, a "Gradle sync failed" banner, an app that will not install) you need to know which file is responsible. This lesson gives you that map.

By the end of this lesson you will be able to:

  • Create a new project with the New Project wizard and the Empty Activity template, and explain each field in the wizard.
  • Explain the difference between Package name, namespace and application ID.
  • Switch between the Android view and the Project view and find any file on disk.
  • Read AndroidManifest.xml and explain the application element, the activity element and the launcher intent filter.
  • Read MainActivity.kt and the generated Compose theme files.
  • Read the two build.gradle.kts files, settings.gradle.kts, gradle.properties, and the version catalog libs.versions.toml.
  • Explain what Gradle sync does and what a build does, step by step.
  • Build an APK from the command line and find it on disk.
  • Fix the most common first-project errors.

How this fits the bigger picture: This lesson matches the Create your first Android app codelab in Unit 1 (the Set up Android Studio pathway) of Google's official Android Basics with Compose course. We follow the same steps, then go much deeper into the files the wizard creates. You will find the official pathway in the resources at the end.

✏️ Your Task
Practise what you just learned about Lesson Introduction. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 2 of 12
Prerequisite Concepts

You need only a handful of ideas. If any feels shaky, follow the lesson reference.

1. Android Studio is installed and a device is not yet needed

You installed Android Studio and at least one SDK platform in lesson 02. Creating a project needs no phone and no emulator. Running it does, and we do that in lesson 04 (emulator) and lesson 05 (real phone). Today you will build the app and look at the result on disk.

2. What a package name looks like

A package name is a "reverse domain" style label such as com.techbase.myfirstapp. Android uses it as a unique identity for your app, much like a national ID number is unique to one person. Two apps on a phone cannot share the same application ID.

3. A little Kotlin

You do not need to understand every line of Kotlin today. Lessons 06 to 14 teach the language properly. If you have not started them yet, read fun greet(name: String) as "a recipe called greet that needs a piece of text called name".

4. What Jetpack Compose is, in one sentence

Jetpack Compose is Android's modern toolkit for describing screens in Kotlin code: you write functions that say what the screen should look like, and Compose draws it. Lessons 15 to 22 build real screens with it.

5. What a build tool is

Source code is not what a phone runs. Something has to compile it, package images and text, and sign the result. That "something" is a build tool. On Android it is Gradle, driven by the Android Gradle Plugin (usually shortened to AGP). Think of Gradle as a factory manager who reads a work order (your build files) and runs the right machines in the right order.

✏️ Your Task
Practise what you just learned about Prerequisite Concepts. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 3 of 12
Conceptual Understanding: Anatomy of an Android Project

Before we click anything, here is the picture to keep in your head.

text
Project  (the whole folder you open in Android Studio)
 |
 |-- settings.gradle.kts      "which modules exist, where do plugins come from"
 |-- build.gradle.kts         project-level: which plugins are available
 |-- gradle/libs.versions.toml  version catalog: one list of versions
 |
 `-- app/                     a MODULE (this one makes the installable app)
      |-- build.gradle.kts    module-level: SDK numbers, dependencies
      `-- src/
           |-- main/          code and resources that ship in the app
           |-- test/          local unit tests (run on your computer)
           `-- androidTest/   instrumented tests (run on a device)

Key vocabulary

Term Meaning Analogy
Project The top-level folder with all your code and build settings. A school compound.
Module A unit of code that builds into one output (an app, or a library). A new project has one module called app. One classroom block inside the compound.
Source set A folder of sources for a purpose: main, test, androidTest. Shelves labelled "for the shop", "for practice", "for inspection".
Resource Non-code files: images, text strings, colours, XML. The decorations and signs, as opposed to the machines.
Manifest AndroidManifest.xml, the app's identity card and declaration of its parts. The registration form an app hands to Android.
Gradle The build tool. The factory manager.
Version catalog libs.versions.toml, a central list of library and plugin versions. The price list pinned on one wall rather than scribbled on every shelf.
APK The installable file the build produces (see lesson 01). The packed carton leaving the factory.

Names that confuse everyone: three kinds of "name"

When you fill the wizard you type one Package name, but it becomes more than one thing. This table saves hours of confusion later.

Name Where it lives What it is for
Package name (wizard field) The folder structure under java/ (for example com/techbase/myfirstapp) and the first line of each Kotlin file (package com.techbase.myfirstapp) Organises your Kotlin classes.
namespace android { namespace = "..." } in the module's build.gradle.kts Tells the build where generated code such as the R resource class lives.
applicationId defaultConfig { applicationId = "..." } in the module's build.gradle.kts The unique identity on a device and on Google Play.

The wizard sets all three to the same value, which is why beginners think they are one thing. Later you may change one without the others. The one that must stay stable once you publish is the applicationId: Google Play treats a different applicationId as a different app.

What the New Project wizard asks

The official documentation describes the wizard's fields like this (labels move slightly between releases, so match by meaning if yours differ):

Field What to enter
Template Choose Empty Activity (phone and tablet). The official guide recommends it as the starting point for Compose projects because it includes the Compose dependencies and Material Design setup.
Name A human name such as My First App. Shown under the icon on the phone.
Package name Becomes your namespace and application ID. Android Studio proposes one based on a placeholder company domain.
Save location A folder on your disk. Avoid very deep paths and, on Windows, paths with unusual characters.
Language Kotlin.
Minimum API level The oldest Android version your app will install on. A lower number reaches more phones but gives you fewer modern features. The Help me choose link shows what share of devices each level covers.

Think about it: If you set the minimum API level very low to reach every old phone, what do you lose? (Newer APIs are not available without extra code, and you have more combinations to test.)

✏️ Your Task
Practise what you just learned about Conceptual Understanding: Anatomy of an Android Project. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 4 of 12
Step-by-Step: Create the Project with the Wizard

Follow along on your own computer. Menu names can differ slightly between Android Studio versions; if an item is not exactly where we say, look for the nearest match.

Step 1: Start the wizard

On the Welcome screen click New Project. If a project is already open, use File > New > New Project from the main menu.

Step 2: Choose a template

The left side lists device types (Phone and Tablet, Wear OS, Television, and so on). Keep Phone and Tablet and pick Empty Activity. Be careful: there are other templates with similar names, such as ones for Views-based projects. The Compose one is the Empty Activity whose description mentions Jetpack Compose. Click Next.

Step 3: Configure the project

Fill in the fields like this:

  1. Name: Hello Techbase
  2. Package name: com.techbase.hellotechbase (use your own name or company domain in real projects).
  3. Save location: a short, simple path such as C:\Users\You\AndroidStudioProjects\HelloTechbase or ~/AndroidStudioProjects/HelloTechbase.
  4. Language: Kotlin.
  5. Minimum API level: keep the suggested default for now. Click Help me choose to see the device-coverage chart.

Click Finish.

Step 4: Wait, and do not panic

The first time, Android Studio does a lot of work:

  • It downloads the Gradle distribution named in the wrapper settings.
  • It downloads libraries (Compose, AndroidX) from Google's Maven repository.
  • It indexes your project so code completion works.

The progress shows at the bottom of the window. On a slow connection or an older laptop this can take several minutes the very first time and may download several hundred megabytes. Later projects reuse the downloads (they are cached in the .gradle folder in your user directory), so they are much faster. Do not close Android Studio while it is syncing. If you are on a metered data plan, do this on Wi-Fi and plan for it.

When the Build tool window says the sync finished and the project tree is visible, you are ready.

What a finished project looks like

In the Project tool window (left side), with the Android view selected, you will see something like this:

text
app
  manifests
    AndroidManifest.xml
  kotlin+java
    com.techbase.hellotechbase
      MainActivity
      ui.theme
        Color.kt
        Theme.kt
        Type.kt
    com.techbase.hellotechbase (androidTest)
      ExampleInstrumentedTest
    com.techbase.hellotechbase (test)
      ExampleUnitTest
  res
    drawable, mipmap-*, values, xml
Gradle Scripts
  build.gradle.kts (Project: Hello_Techbase)
  build.gradle.kts (Module :app)
  gradle-wrapper.properties
  libs.versions.toml
  proguard-rules.pro
  gradle.properties
  local.properties
  settings.gradle.kts

Your tree may differ in small ways (for example the group might be labelled java or kotlin+java, and the Gradle Scripts list changes between versions). The names and ideas stay the same.

✏️ Your Task
Practise what you just learned about Step-by-Step: Create the Project with the Wizard. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 5 of 12
The Two Views: Android and Project

The Project tool window can show your files in more than one way. The selector is at the top of the tool window.

View What it shows Use it when
Android A tidy, grouped view: manifests, source code, resources and a Gradle Scripts group. It hides folders you rarely need and groups related files. Everyday work. This is the default.
Project The real folders exactly as they exist on disk, including build/ and .gradle/. You need to find a generated file (such as an APK) or want to see the true layout.

Switch to Project view now and expand the folders. This is the real disk layout:

text
HelloTechbase/
|-- .gradle/                    Gradle's cache for this project (do not edit)
|-- .idea/                      IDE settings (usually not committed)
|-- app/
|   |-- build/                  everything the build produced (APK lives here)
|   |-- libs/                   private library files, rarely used
|   |-- src/
|   |   |-- androidTest/        tests that run on a device
|   |   |-- main/
|   |   |   |-- AndroidManifest.xml
|   |   |   |-- java/com/techbase/hellotechbase/   Kotlin sources
|   |   |   `-- res/                               resources
|   |   `-- test/               tests that run on your computer
|   |-- build.gradle.kts        module build file
|   `-- proguard-rules.pro      rules for code shrinking (lesson 49)
|-- build/                      project-level build output
|-- gradle/
|   |-- libs.versions.toml      version catalog
|   `-- wrapper/
|       `-- gradle-wrapper.properties   which Gradle version to use
|-- build.gradle.kts            project build file
|-- gradle.properties           Gradle and project settings
|-- gradlew, gradlew.bat        wrapper scripts (macOS/Linux, Windows)
|-- local.properties            this computer's SDK path (never share)
`-- settings.gradle.kts         names modules, sets repositories

Notice that Kotlin source files may live under a folder named java, even though they are Kotlin. That is a long-standing convention: the folder name is java, but it holds both languages. A kotlin folder is also accepted.

Why this matters: An APK you want to share will be found under app/build/outputs/apk/debug/. The Android view hides that folder, but the Project view shows it.

✏️ Your Task
Practise what you just learned about The Two Views: Android and Project. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 6 of 12
Reading AndroidManifest.xml

Open app/src/main/AndroidManifest.xml. The manifest declares to Android everything the system must know before it runs any of your code: the app's parts, permissions it needs, and what to show in the launcher.

xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools">
 <application
 android:allowBackup="true"
 android:dataExtractionRules="@xml/data_extraction_rules"
 android:fullBackupContent="@xml/backup_rules"
 android:icon="@mipmap/ic_launcher"
 android:label="@string/app_name"
 android:roundIcon="@mipmap/ic_launcher_round"
 android:supportsRtl="true"
 android:theme="@style/Theme.HelloTechbase">
 <activity
 android:name=".MainActivity"
 android:exported="true"
 android:label="@string/app_name"
 android:theme="@style/Theme.HelloTechbase">
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>
 </application>
</manifest>

The exact attributes in your file may differ a little between Android Studio versions, but the structure is stable.

Block-by-block explanation

  • manifest is the root element. The xmlns: lines declare XML namespaces so that names such as android:label are understood. You never edit them.
  • application describes the whole app. android:icon and android:roundIcon point at the launcher icons in the mipmap folders. android:label is the name shown under the icon, read from strings.xml. android:theme is the starting look. android:supportsRtl="true" allows right-to-left languages such as Arabic. allowBackup, dataExtractionRules and fullBackupContent control cloud backup and device-transfer of app data.
  • activity declares one screen entry point. An Activity is one of Android's building blocks that shows a window. android:name=".MainActivity" means the class MainActivity in your package (the leading dot means "in my package").
  • android:exported="true" says other apps, including the launcher, may start this activity. Modern Android requires you to state this explicitly for any component that has an intent filter.
  • intent-filter tells Android what this activity can respond to. The pair MAIN plus LAUNCHER means: "this is the main entry point, and it should appear in the app launcher". Remove it and the app installs but has no icon to tap.

Later lessons add things to this file: permissions such as internet access (lesson 29), more activities, services, and so on. For now, remember: if a component is not declared here, Android does not know it exists.

A tiny experiment: change the app name

Open app/src/main/res/values/strings.xml:

xml
<resources>
 <string name="app_name">Hello Techbase</string>
</resources>

Because the manifest uses @string/app_name, changing this text changes the name under the icon with no code edits. Keeping words in strings.xml instead of hard-coding them is also how apps are translated into other languages (for example Yoruba or Hausa) later.

✏️ Your Task
Practise what you just learned about Reading AndroidManifest.xml. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 7 of 12
Reading MainActivity.kt and the Theme Files

Open MainActivity.kt (under java/com/techbase/hellotechbase). Current templates generate something very close to this:

kotlin
package com.techbase.hellotechbase
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.techbase.hellotechbase.ui.theme.HelloTechbaseTheme
class MainActivity : ComponentActivity() {
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 enableEdgeToEdge()
 setContent {
 HelloTechbaseTheme {
 Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
 Greeting(
 name = "Android",
 modifier = Modifier.padding(innerPadding)
 )
 }
 }
 }
 }
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
 Text(
 text = "Hello $name!",
 modifier = modifier
 )
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
 HelloTechbaseTheme {
 Greeting("Android")
 }
}

Your generated file may differ slightly (for example it may not call enableEdgeToEdge() in older versions). Do not worry about details; read it in blocks.

Block-by-block explanation

Part Plain-English meaning
package ... The folder this file belongs to. Matches the folders on disk.
import ... Brings in tools you use. Without imports Kotlin does not know where Text or Modifier come from.
class MainActivity : ComponentActivity() Your screen. ComponentActivity is a ready-made base class from AndroidX that knows how to host Compose.
override fun onCreate(...) Android calls this when it creates the screen. It is the equivalent of main for a screen. Lesson 23 explains the lifecycle in full.
enableEdgeToEdge() Lets your app draw behind the status bar and navigation bar for a modern look. The Scaffold padding keeps content clear of them.
setContent { ... } "Here is what the screen shows", written with Compose instead of an XML layout.
HelloTechbaseTheme { ... } Your Material 3 theme wrapper. Everything inside gets its colours and fonts.
Scaffold A Material layout skeleton that handles things like top bars. Here it supplies innerPadding.
@Composable fun Greeting A composable function: a function that describes a piece of UI.
@Preview Shows the composable inside Android Studio without running the app.

The ui/theme folder

Open the ui/theme package. Three files live there:

  • Color.kt defines colour values.
  • Type.kt defines text styles (fonts, sizes).
  • Theme.kt defines HelloTechbaseTheme, which combines colours, typography and shapes into a Material 3 theme.

Lesson 22 (Material Theming) and lesson 40 (Material Design 3) go deep on these. For now, know where they are.

See it in the Preview (no emulator needed)

Open MainActivity.kt and look for the Code / Split / Design buttons at the top right of the editor. Choose Split. Android Studio renders GreetingPreview beside your code and shows a white card saying "Hello Android!". If you see "Build & Refresh" or a banner saying the preview is out of date, click it. Previews are the quickest way to see Compose changes, and they need far less RAM than an emulator.

✏️ Your Task
Practise what you just learned about Reading MainActivity.kt and the Theme Files. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 8 of 12
Reading the Gradle Files

This is the part beginners skip and later regret. Open each file as we go. Remember that the numbers (SDK levels and library versions) change with each Android Studio release, so we show the shape and tell you how to read the numbers.

settings.gradle.kts

kotlin
pluginManagement {
 repositories {
 google {
 content {
 includeGroupByRegex("com\\.android.*")
 includeGroupByRegex("com\\.google.*")
 includeGroupByRegex("androidx.*")
 }
 }
 mavenCentral()
 gradlePluginPortal()
 }
}
dependencyResolutionManagement {
 repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
 repositories {
 google()
 mavenCentral()
 }
}
rootProject.name = "Hello Techbase"
include(":app")

This file runs first. It answers three questions: Where do build plugins come from? (pluginManagement), Where do libraries come from? (dependencyResolutionManagement: Google's repository and Maven Central), and What is in this project? (include(":app") adds the app module). If you add a module later, you add an include line here (Android Studio does it for you).

Project-level build.gradle.kts

kotlin
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
 alias(libs.plugins.android.application) apply false
 alias(libs.plugins.kotlin.compose) apply false
}

This file is short on purpose. It declares which plugins the modules may use, with apply false meaning "make it available, but do not apply it to the root project itself". Notice alias(libs.plugins.…): the plugin IDs and versions are not written here but looked up in the version catalog. Depending on your Android Studio and AGP version you may also see a Kotlin Android plugin line; newer AGP releases build Kotlin support in. Do not delete lines you do not recognise.

Module-level app/build.gradle.kts

This is the file you will edit most. A representative version:

kotlin
plugins {
 alias(libs.plugins.android.application)
 alias(libs.plugins.kotlin.compose)
}
android {
 namespace = "com.techbase.hellotechbase"
 compileSdk = 36 // illustrative: yours may be higher
 defaultConfig {
 applicationId = "com.techbase.hellotechbase"
 minSdk = 24 // illustrative: from the wizard
 targetSdk = 36 // illustrative
 versionCode = 1
 versionName = "1.0"
 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
 }
 buildTypes {
 release {
 isMinifyEnabled = false
 proguardFiles(
 getDefaultProguardFile("proguard-android-optimize.txt"),
 "proguard-rules.pro"
 )
 }
 }
 compileOptions {
 sourceCompatibility = JavaVersion.VERSION_11
 targetCompatibility = JavaVersion.VERSION_11
 }
 buildFeatures {
 compose = true
 }
}
dependencies {
 implementation(libs.androidx.core.ktx)
 implementation(libs.androidx.lifecycle.runtime.ktx)
 implementation(libs.androidx.activity.compose)
 implementation(platform(libs.androidx.compose.bom))
 implementation(libs.androidx.ui)
 implementation(libs.androidx.ui.graphics)
 implementation(libs.androidx.ui.tooling.preview)
 implementation(libs.androidx.material3)
 testImplementation(libs.junit)
 androidTestImplementation(libs.androidx.junit)
 androidTestImplementation(libs.androidx.espresso.core)
 androidTestImplementation(platform(libs.androidx.compose.bom))
 androidTestImplementation(libs.androidx.ui.test.junit4)
 debugImplementation(libs.androidx.ui.tooling)
 debugImplementation(libs.androidx.ui.test.manifest)
}

Your generated file may be organised a little differently (for example newer templates set compileSdk through a block and may include a kotlin { } block for the JVM target). Treat the following as the permanent ideas.

Setting What it means
plugins { ... } Switches on the Android application plugin and the Compose compiler plugin (org.jetbrains.kotlin.plugin.compose), which is what makes @Composable work.
namespace Where generated code such as R lives.
compileSdk The Android SDK version your code is compiled against. It decides which APIs you can call. Use the newest stable.
minSdk The oldest Android API level the app installs on.
targetSdk The Android version you have tested the app's behaviour against. Google Play requires recent values for new apps and updates (lesson 49).
applicationId Your app's unique identity.
versionCode A whole number that must go up with every release you upload. Users never see it.
versionName The human-friendly version such as 1.0.
buildTypes debug (for you, automatic) and release (for the Play Store).
buildFeatures { compose = true } Turns on Compose build support (older templates also set composeOptions).
dependencies The libraries your app uses.

Reading dependency lines

  • implementation(...) adds a library to your app's main code.
  • testImplementation(...) adds it only to local unit tests.
  • androidTestImplementation(...) adds it only to device tests.
  • debugImplementation(...) adds it only to debug builds (for tooling you do not ship).
  • platform(libs.androidx.compose.bom) imports a BOM (bill of materials): one version number that keeps all Compose libraries on versions that work together. That is why the other Compose lines have no version.

gradle/libs.versions.toml: the version catalog

Open gradle/libs.versions.toml (visible in the Android view under Gradle Scripts). It centralises versions in one place:

toml
[versions]
agp = "x.y.z" # Android Gradle Plugin version (yours is generated)
kotlin = "x.y.z"
coreKtx = "x.y.z"
composeBom = "yyyy.mm.dd"
junit = "x.y.z"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

We wrote x.y.z because the real numbers are generated for you and change often. Never type old version numbers from a blog post; use the ones Android Studio generated, and update them with the IDE's suggestions.

How the names connect:

In the TOML file In a build file
androidx-core-ktx under [libraries] libs.androidx.core.ktx
android-application under [plugins] alias(libs.plugins.android.application)
junit under [libraries] libs.junit

Dashes in the TOML name become dots in code. The [versions] table avoids repeating a number: change composeBom once and every Compose library moves together. A fourth section, [bundles], lets you group several libraries under one name; you will meet it in later lessons.

To add a library later, you add a line under [libraries], then add implementation(libs.your.name) in app/build.gradle.kts, then sync. Android Studio's File > Project Structure > Dependencies screen can also do this for you.

The remaining files

File Purpose
gradle.properties Settings for Gradle itself, such as how much memory the Gradle JVM may use and whether to use AndroidX. Shared with your team.
local.properties Contains your computer's SDK path, for example sdk.dir=.... Machine-specific. Never commit it or share it.
gradle/wrapper/gradle-wrapper.properties Names the exact Gradle version to download. This is why you never install Gradle yourself.
gradlew and gradlew.bat The Gradle Wrapper scripts. Running ./gradlew (macOS/Linux) or gradlew.bat (Windows) uses the right Gradle version automatically.
proguard-rules.pro Rules for code shrinking in release builds (lesson 49).
.gitignore Tells Git which files not to track (build/, local.properties, and so on).
✏️ Your Task
Practise what you just learned about Reading the Gradle Files. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 9 of 12
Gradle Sync and What a Build Does

Gradle sync

When you open a project or edit a build file, Android Studio shows a banner saying Sync Now, or starts a sync automatically. Gradle sync does not build your app. It reads the build files, works out the modules, plugins and dependencies, downloads whatever is missing, and then tells the IDE what the project looks like so it can offer code completion and error checking.

Think of sync as a builder reading the plan and checking the materials are in stock. Construction (the build) is a separate step. Whenever you change anything in a .gradle.kts file or libs.versions.toml, sync again. The elephant icon in the toolbar (labelled Sync Project with Gradle Files) does it.

The three phases of a Gradle run

  1. Initialisation: Gradle reads settings.gradle.kts to find out which modules exist.
  2. Configuration: Gradle reads each build.gradle.kts and builds a graph of tasks (small jobs) and their order.
  3. Execution: Gradle runs the tasks that are needed, skipping any whose inputs have not changed. That skipping is why the second build is much quicker than the first.

What actually happens when you build an app

Here is the simplified production line for a debug build:

text
Kotlin sources (.kt)  --kotlinc-->  .class files (JVM bytecode)
Resources (res/)      --aapt2--->   compiled resources + R class
Manifest              --merge--->   one final manifest (with libraries' manifests)
.class + libraries    --D8------>   classes.dex  (Android's bytecode)
dex + resources       --package->   unsigned APK
unsigned APK          --sign---->   debug-signed APK  (app-debug.apk)

The tools in the middle have names worth recognising: kotlinc compiles Kotlin, AAPT2 processes resources, D8 converts Java bytecode to DEX (the format ART runs, from lesson 01), and the final step signs the package with an automatically created debug key. Release builds add shrinking with R8 and use your own signing key (lesson 49).

Build from the command line

You can run the same factory without clicking. Open the Terminal tool window at the bottom of Android Studio (it opens in your project root) and run:

bash
./gradlew assembleDebug

On Windows use:

bash
gradlew.bat assembleDebug

A successful run ends with a message like this (task counts and times will differ):

BUILD SUCCESSFUL in 1m 12s
33 actionable tasks: 33 executed

Then look in the Project view:

text
app/build/outputs/apk/debug/app-debug.apk

That file is a real, installable Android app. Other handy tasks:

bash
./gradlew tasks # list available tasks
./gradlew clean # delete the build/ folders
./gradlew installDebug # build and install on a connected device or emulator
./gradlew test # run local unit tests

From the IDE, Build > Make Project compiles, Build > Clean Project deletes build outputs, and Build > Build Bundle(s) / APK(s) produces packages. The Run button builds and installs and launches.

Debug and release, briefly

The debug build type is signed automatically and is not optimised, so it is easy to debug but larger and slower. The release build type is what you upload to Google Play after shrinking and signing with your own key. You will build one in lesson 49.

✏️ Your Task
Practise what you just learned about Gradle Sync and What a Build Does. Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 10 of 12
Guided Practice Exercises
🎯 Your Challenge

Exercise 1: Change the greeting and the app name

Objective: Prove you can edit code and resources and see the result in the Preview.

Steps:

  1. Open strings.xml and change app_name to Greeter.
  2. In MainActivity.kt, change the name passed to Greeting from "Android" to your own first name.
  3. Change the text so it reads Welcome, <name>! (with your actual name) instead of Hello <name>!.
  4. Check the result in Split view without running the app.

strings.xml:

xml
<resources>
 <string name="app_name">Greeter</string>
</resources>

Greeting with a string template (see lesson 06):

kotlin
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
 Text(
 text = "Welcome, $name!",
 modifier = modifier
 )
}

And in setContent, pass your name:

kotlin
Greeting(
 name = "Tunde",
 modifier = Modifier.padding(innerPadding)
)

The preview function GreetingPreview still passes "Android", so the Preview shows Welcome, Android!. Change it too if you want it to match.

🎯 Your Challenge

Exercise 2: Add a library using the version catalog

Objective: Practise the three-file workflow (catalog, build file, sync) using a library that is safe to add.

Add the AndroidX Core SplashScreen library, or any small AndroidX library of your choice, using the version catalog. First look up the latest stable version on the library's official page (search for "androidx core splashscreen release notes" on developer.android.com) and never guess it. Describe where each of the three edits goes.

  1. In gradle/libs.versions.toml, add a version and a library (use the real latest stable version number in place of x.y.z):
toml
[versions]
splashscreen = "x.y.z"
[libraries]
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splashscreen" }
  1. In app/build.gradle.kts, inside dependencies:
kotlin
implementation(libs.androidx.core.splashscreen)
  1. Click Sync Now in the banner. If the sync succeeds, the dependency is available. If it fails with "could not resolve", the version number you typed does not exist, so check it against the official page.

You edited exactly three things: the catalog, the module build file, and then you ran a sync. The dash-to-dot rule turned androidx-core-splashscreen into libs.androidx.core.splashscreen.

🎯 Your Challenge

Exercise 3: Build an APK from the terminal and inspect it

Open the Terminal tool window, build the debug APK with Gradle, then answer: Where is the file? What is its approximate size? Why is it larger than a hand-written "Hello" app from the 2010s? (Think about what libraries Compose brings.)

Run:

bash
./gradlew assembleDebug

(gradlew.bat assembleDebug on Windows.) The file is at app/build/outputs/apk/debug/app-debug.apk. In the Project view you can right-click it and choose Reveal in Finder / Show in Explorer / Open in Files to see the size. A fresh Compose debug APK is typically several megabytes. Exact numbers vary with versions, so do not memorise one.

It is large for a "Hello" app because a debug build is not shrunk, and it includes the Compose runtime, Material 3 and AndroidX libraries. Release builds with R8 shrinking are much smaller (lesson 49).

✏️ Your Task

Switch the Project tool window to Project view and write down the full path to three things: your MainActivity.kt, your AndroidManifest.xml, and the folder where the APK is created. Then switch back to the Android view and note where the same files appear. Being able to translate between the two views is a skill you will use daily.

Phase 11 of 12
Common Beginner Mistakes (and How to Fix Them)

Mistake #1: Editing a build file and forgetting to sync. The wrong action: adding implementation(libs.something) and then seeing red text in Kotlin code. The problem: the IDE has not learned about the new library yet. The fix: click Sync Now (or the elephant icon) and wait for it to finish.

Mistake #2: Typing a version that does not exist. The wrong code: composeBom = "2020.01.01" copied from an old blog. The problem: old or invented versions fail to resolve or clash with the Kotlin and AGP versions. The fix: use versions Android Studio generated, or look them up on the library's official release page.

Mistake #3: Deleting exported or the intent filter. The wrong code: removing the intent-filter block "to clean up". The problem: the app installs but there is no launcher icon, and an app that targets Android 12 or higher will not install if an activity has an intent filter but no android:exported value. The fix: restore the MAIN and LAUNCHER filter and android:exported="true" on the launcher activity.

Mistake #4: Renaming the package by hand in the file explorer. The wrong action: renaming folders in your operating system. The problem: the package line at the top of every file, the manifest, namespace and applicationId all go out of date, producing a flood of errors. The fix: rename through Android Studio's Refactor > Rename on the package, and then update namespace and applicationId deliberately. Or, create a fresh project with the right name while you are still learning.

Mistake #5: Committing local.properties. The wrong action: sharing the file with the project. The problem: it contains your computer's SDK path, which is wrong on every other machine. The fix: leave it out of version control; the generated .gitignore already does.

Mistake #6: Treating a Gradle sync failure as a disaster. The symptom: a red banner. The fix: open the Build tool window and read the first error line. The usual causes are: no internet while downloading, a typo in a catalog name, or a Java/Gradle version mismatch. Try File > Sync Project with Gradle Files after fixing, and as a last resort Build > Clean Project or File > Invalidate Caches (menu names move; look for the nearest match).

Mistake #7: Choosing the wrong template. The symptom: the project has no @Composable code and no Compose lines in dependencies. The fix: you picked a Views-based template. Start again and choose the Empty Activity template that mentions Jetpack Compose.

Mistake #8: Running out of disk space or memory. The symptom: sync or indexing takes forever or fails. The fix: free several gigabytes of disk, close browsers and other heavy programs, and make sure the project is on a normal drive, not a network folder. See the performance tips in lesson 02.

✏️ Your Task
Practise what you just learned about Common Beginner Mistakes (and How to Fix Them). Open Android Studio, type the examples by hand, change a value, and observe what happens.
Phase 12 of 12
Knowledge Check

Answer each question in your head (or on paper), then reveal the answer.

Question 1. What are the three different "names" created from the wizard's Package name, and which one must stay the same after you publish?

The package (folder and package line in Kotlin files), the namespace (where generated code such as R lives) and the applicationId (the app's unique identity on devices and Google Play). The applicationId must stay the same after you publish, because Google Play treats a new ID as a new app.

Question 2. In AndroidManifest.xml, which element combination makes an activity appear as the launcher icon's target?

An intent-filter containing the action android.intent.action.MAIN and the category android.intent.category.LAUNCHER, on an activity that also has android:exported="true".

Question 3. What is the difference between compileSdk, minSdk and targetSdk?

compileSdk is the SDK version your code is compiled against (which APIs you can call). minSdk is the oldest Android API level your app installs on. targetSdk is the Android version whose behaviour you have tested and opted into.

Question 4. What does Gradle sync do, and what does it not do?

Sync reads the build files, resolves modules, plugins and dependencies, downloads what is missing, and updates the IDE's understanding of the project. It does not compile or package your app; that is the build.

Question 5. In a version catalog, what does androidx-core-ktx under [libraries] become in a build file, and why use a catalog at all?

It becomes libs.androidx.core.ktx (dashes become dots). A catalog keeps every version in one file, so you change a number once instead of hunting through build files, and it gives you code completion for dependencies and plugins.

Go deeper: official resources

✏️ Your Task
Practise what you just learned about Knowledge Check. Open Android Studio, type the examples by hand, change a value, and observe what happens.
🏗️ Build It — Mini Project
Project Anatomy Report

Project goal: Create a fresh Android project and produce a short written "anatomy report" that proves you understand every important file. Then make three small changes and build the APK from the terminal. You will show that you can navigate, edit and build a project, not merely run it.

Requirements:

  1. Create a project named Anatomy Lab with the Empty Activity (Compose) template and the package name com.techbase.anatomylab.
  2. Create a text file (in any note app) with these headings and fill each with one or two sentences in your own words: Manifest, MainActivity, Module build.gradle.kts, Project build.gradle.kts, settings.gradle.kts, libs.versions.toml, local.properties.
  3. Record these values from your project: namespace, applicationId, minSdk, targetSdk, compileSdk, versionCode, versionName.
  4. Make three changes: change the app name in strings.xml; change the text in Greeting; change versionName to "0.1-lab" and versionCode to 2.
  5. Sync, then run ./gradlew assembleDebug (or gradlew.bat assembleDebug) in the Terminal tool window.
  6. Write down the path and size of the APK that was created.

Stretch goal: Add a second composable function Farewell(name: String) that shows Goodbye, <name>! and display both greetings in a Column. Check both in the Preview.

A sample anatomy report (your values will differ because they depend on your Android Studio version):

text
ANATOMY LAB REPORT

Manifest: declares the application, the MainActivity entry point, and the
MAIN + LAUNCHER intent filter that gives the app its icon.
MainActivity: a ComponentActivity whose onCreate calls setContent to show
the Compose UI wrapped in AnatomyLabTheme.
Module build.gradle.kts: SDK numbers, version info, build types, Compose
switch and the dependency list.
Project build.gradle.kts: makes the plugins available to modules (apply false).
settings.gradle.kts: where plugins and libraries are downloaded from, and
which modules are included (:app).
libs.versions.toml: the central list of versions, libraries and plugins.
local.properties: this computer's SDK path. Never committed.

Recorded values:
  namespace      com.techbase.anatomylab
  applicationId  com.techbase.anatomylab
  minSdk         (from your wizard choice)
  targetSdk      (generated)
  compileSdk     (generated)
  versionCode    1  ->  2
  versionName    "1.0" -> "0.1-lab"

APK: app/build/outputs/apk/debug/app-debug.apk  (several MB)

The three edits:

xml
<resources>
 <string name="app_name">Anatomy Lab</string>
</resources>
kotlin
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
 Text(
 text = "Welcome to the lab, $name!",
 modifier = modifier
 )
}
kotlin
defaultConfig {
 applicationId = "com.techbase.anatomylab"
 versionCode = 2
 versionName = "0.1-lab"
 // other lines unchanged
}

Stretch goal solution in MainActivity.kt:

kotlin
package com.techbase.anatomylab
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.techbase.anatomylab.ui.theme.AnatomyLabTheme
@Composable
fun Farewell(name: String, modifier: Modifier = Modifier) {
 Text(text = "Goodbye, $name!", modifier = modifier)
}
@Composable
fun GreetingPair(name: String, modifier: Modifier = Modifier) {
 Column(modifier = modifier) {
 Greeting(name = name)
 Farewell(name = name)
 }
}
@Preview(showBackground = true)
@Composable
fun GreetingPairPreview() {
 AnatomyLabTheme {
 GreetingPair("Tunde")
 }
}

In setContent, replace the Greeting(...) call with GreetingPair(name = "Tunde", modifier = Modifier.padding(innerPadding)). The Preview shows two lines stacked vertically.

Expected terminal result:

BUILD SUCCESSFUL in 45s
33 actionable tasks: 33 executed

1

Open your finished project in Android Studio. Check that the .gitignore file the wizard created is in the project root. It keeps local.properties, build folders and keystores out of Git. Never commit passwords or signing keys.

2

Choose Git > Create Git Repository… (or VCS > Enable Version Control Integration in older versions), select the project folder, then open Git > Commit, tick every file, and write the message “Add Android lesson 03 project”.

3

On github.com click New repository, name it android-lesson-03-create-your-first-android-app, set it to Public, leave Add a README unticked, and click Create repository.

4

Back in Android Studio choose Git > Manage Remotes…, add the repository URL as origin, then choose Git > Push… and push your main branch. Prefer the terminal? Run git remote add origin <your-url> then git push -u origin main.

5

Add a README.md with a screenshot of your app running and a short description. Employers and clients read READMEs first. Your project is now part of your public portfolio. 🎉

Lesson 03 complete! 🎉

You covered:

  • Lesson Introduction
  • Prerequisite Concepts
  • Conceptual Understanding: Anatomy of an Android Project
  • Step-by-Step: Create the Project with the Wizard
  • The Two Views: Android and Project
  • Reading AndroidManifest.xml
  • Reading MainActivity.kt and the Theme Files
  • Reading the Gradle Files
  • Gradle Sync and What a Build Does
  • Guided Practice Exercises
  • Common Beginner Mistakes (and How to Fix Them)
  • Knowledge Check
All 72 lessons in Google Android Studio ·Techbase Academy ·© 2025–2026 Techbase Consultant Services, Ibadan
Esc
↑ ↓ to moveEnter to openEsc to close