Project Directories
Project Directory Structure
An Android project follows a standardized directory layout:
MyApp/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/myapp/ # Kotlin/Java source
│ │ │ ├── res/ # Resources
│ │ │ │ ├── layout/ # XML layouts
│ │ │ │ ├── values/ # Strings, colors, styles
│ │ │ │ ├── drawable/ # Images and shapes
│ │ │ │ ├── mipmap/ # App icons
│ │ │ │ └── xml/ # Other XML configs
│ │ │ └── AndroidManifest.xml # App manifest
│ │ ├── debug/ # Debug-specific sources
│ │ └── release/ # Release-specific sources
│ ├── build.gradle.kts # Module build config
│ └── proguard-rules.pro # ProGuard/R8 rules
├── build.gradle.kts # Project-level build config
├── settings.gradle.kts # Project settings
├── gradle.properties # Gradle properties
└── gradle/
└── wrapper/
├── gradle-wrapper.jar
└── gradle-wrapper.properties # Gradle version
Source Sets
The src/ directory contains source sets — collections of source files and resources grouped by build variant:
- main: Default source set used by all build types
- debug: Debug-only code (e.g., Stetho initialization)
- release: Release-only code (e.g., ProGuard configuration)
- androidTest: Instrumentation tests running on devices
- test: Unit tests running on JVM
// src/debug/java/com/example/myapp/DebugInitializer.kt
// Only included in debug builds
object DebugInitializer {
fun init() {
// Enable leak detection, logging, etc.
}
}
Package Structure Convention
Organize code by feature, not by layer:
com.example.myapp/
├── data/
│ ├── remote/
│ ├── local/
│ └── repository/
├── domain/
│ ├── model/
│ └── usecase/
├── ui/
│ ├── home/
│ ├── detail/
│ └── settings/
└── util/
This scales better than organizing by type (Activity/, Fragment/, Adapter/) because related files live together. When you add a feature, you create one directory instead of scattering files across five.
Gradle Configuration
Gradle Configuration
Project-Level vs Module-Level
The project-level build.gradle.kts configures plugins and repositories shared across all modules:
// build.gradle.kts (project)
plugins {
id("com.android.application") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
}
The module-level build.gradle.kts configures the specific app module:
// app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
kotlin("kapt")
}
android {
namespace = "com.example.myapp"
compileSdk = 35
defaultConfig {
applicationId = "com.example.myapp"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
buildTypes {
debug {
isMinifyEnabled = false
applicationIdSuffix = ".debug"
}
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
flavorDimensions += "environment"
productFlavors {
create("dev") {
dimension = "environment"
applicationIdSuffix = ".dev"
}
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
}
create("production") {
dimension = "environment"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
viewBinding = true
}
}
Build Variants
Build variants combine a build type (debug/release) with a product flavor. For three flavors and two build types, you get six variants: devDebug, devRelease, stagingDebug, etc.
Dependency Management
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
}
Dependency configurations:
implementation— Not exposed to dependent modules (faster builds)api— Exposed to dependent modules (use sparingly)compileOnly— Available at compile time onlyruntimeOnly— Available at runtime onlykapt/ksp— Annotation processing
Manifest and Resources
AndroidManifest.xml
The manifest declares your app's components, permissions, and configuration:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".MyApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.MyApp">
<activity
android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.DetailActivity" />
<service
android:name=".service.SyncService"
android:exported="false" />
</application>
</manifest>
Resource Directories
res/
├── layout/ # UI layouts
├── values/ # Default values
│ ├── strings.xml
│ ├── colors.xml
│ ├── dimens.xml
│ └── styles.xml
├── values-night/ # Dark theme overrides
├── values-sw600dp/ # Tablet overrides
├── drawable/ # Vector drawables, shapes
├── mipmap-xxxhdpi/ # App icons (high density)
├── xml/ # Preference screens, backup rules
├── raw/ # Raw files (audio, JSON)
└── navigation/ # Navigation graph
Resource Qualifiers
Qualifiers let you provide alternative resources for different configurations:
| Qualifier | Example | Description |
|---|---|---|
values-sw600dp |
values-sw600dp/ |
Tablets (7" and larger) |
values-night |
values-night/ |
Dark theme |
layout-land |
layout-land/ |
Landscape orientation |
drawable-hdpi |
drawable-hdpi/ |
High density screens |
values-fr |
values-fr/ |
French locale |
The system selects the best-matching resource at runtime based on device configuration. This is how Android handles responsive design across screen sizes, orientations, languages, and themes without writing conditional code.
Quiz
1. What is the purpose of the settings.gradle.kts file?
2. What does isMinifyEnabled do in a release build type?
3. What is the difference between implementation and api dependency configurations?
4. Why should you organize code by feature rather than by type?
Flashcards
Question
What is the difference between project-level and module-level build.gradle.kts?
Click to reveal answer
Answer
Project-level configures plugins and repositories shared across all modules. Module-level configures SDK versions, dependencies, and build types for a specific module.
Question
What does the applicationId do in defaultConfig?
Click to reveal answer
Answer
It uniquely identifies your app on the device and Play Store. It can differ from your package name and supports suffixes per build variant (e.g., .debug).
Question
What are the standard source sets in an Android module?
Click to reveal answer
Answer
main (default), debug, release, androidTest (instrumentation tests), and test (unit tests). Each can have its own sources and resources.
Question
What is the purpose of proguard-rules.pro?
Click to reveal answer
Answer
It contains rules for R8/ProGuard code shrinking, obfuscation, and optimization during release builds. Rules specify which classes to keep or remove.
Revision Notes
Key Takeaways
- 1. Organize code by feature, not by type, for better scalability
- 2. Project-level build.gradle.kts configures plugins; module-level configures the app
- 3. isMinifyEnabled enables R8 shrinking and obfuscation for release builds
- 4. Resource qualifiers provide automatic configuration-based resource selection
- 5. implementation hides dependencies from consumers; api exposes them
Interview Tips
- • Explain the difference between applicationId and package name
- • Know how build variants work (flavor x build type combinations)
- • Understand why feature-based package structure scales better
- • Be ready to discuss dependency configurations and when to use each
Cheat Sheet
Project Structure Cheat Sheet
Key Files:
settings.gradle.kts— declares included modulesbuild.gradle.kts(project) — plugins, shared configbuild.gradle.kts(module) — SDK versions, dependenciesAndroidManifest.xml— app components, permissions
Build Types:
debug— unoptimized, debuggable, .debug suffixrelease— minified, obfuscated, R8 enabled
Dependency Configs:
implementation— internal, fast buildsapi— transitive, use sparinglycompileOnly— compile time onlykapt/ksp— annotation processing
Resource Qualifiers:
values-night— dark themevalues-sw600dp— tabletlayout-land— landscapedrawable-hdpi— screen density