Android Fundamentals with Java

Android Project Structure

18 min Lesson 3 of 12

Android Project Structure

When Android Studio generates a new project it creates a specific directory layout that every Android developer must understand. The layout is not arbitrary — each folder maps to a concept in the build pipeline. Knowing where things live and why tells you where to make changes and what happens when you do.

The Top-Level View

At the root of the project you will see two distinct layers: the project level and the app module level. Android projects are multi-module by default (even if your app only has one module). The outermost build.gradle and settings.gradle files belong to the project layer; the app/ directory is your actual application module.

MyAndroidApp/ ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/example/myapp/ ← Java source files │ │ │ ├── res/ ← Resources (layouts, drawables, strings …) │ │ │ └── AndroidManifest.xml ← App declaration │ │ └── test/ ← Unit tests (JVM) │ │ └── androidTest/ ← Instrumented tests (device/emulator) │ └── build.gradle ← Module-level Gradle build script ├── gradle/ │ └── wrapper/ │ └── gradle-wrapper.properties ← Gradle version pin ├── build.gradle ← Project-level Gradle build script └── settings.gradle ← Module list & plugin management

The Java Source Tree

All of your application code lives under app/src/main/java/ in a standard Java package hierarchy. Android Studio creates a default package based on the Application ID you entered when creating the project (e.g. com.example.myapp). Every Activity, Fragment, Service, BroadcastReceiver, and helper class you write goes here.

Package name = Application ID (usually). The package declared in your Java files and the applicationId in build.gradle are independent, but keeping them the same avoids confusion. The Application ID is what uniquely identifies your app on the Play Store and on-device; you cannot change it after publishing.

The res/ Directory

Android separates code from resources. Everything visual or configurable — layouts, images, colours, strings — lives under res/ in type-named subdirectories. The build system compiles each resource into a numeric ID and generates the R class so your Java code can reference resources by name at compile time.

  • res/layout/ — XML files that describe the view hierarchy of each screen or fragment.
  • res/values/ — XML files for named scalars: strings.xml, colors.xml, dimens.xml, styles.xml.
  • res/drawable/ — Vector drawables and other graphics (PNG, XML shape files).
  • res/mipmap-*/ — Launcher icons at various densities (hdpi, xhdpi, xxhdpi …).
  • res/menu/ — XML menu definitions for options menus and context menus.
  • res/anim/ — Tween animation XML files.

Accessing a string resource from Java looks like this:

// In an Activity: String appName = getString(R.string.app_name); // In a layout XML file (res/layout/activity_main.xml): // android:text="@string/app_name"
Never hardcode user-visible text. Always declare strings in res/values/strings.xml and reference them via R.string.*. This is the foundation of localisation — adding res/values-ar/strings.xml for Arabic is all it takes to support a new language.

AndroidManifest.xml

The AndroidManifest.xml at app/src/main/AndroidManifest.xml is the single most important configuration file in your project. Before the Android OS will run any component of your app, that component must be declared here. The manifest also declares:

  • The application's package name (used to resolve relative class names).
  • Every <activity>, <service>, <receiver>, and <provider> in the app.
  • The launcher Activity — the one that starts when the user taps the icon — via an <intent-filter> with action MAIN and category LAUNCHER.
  • Permissions the app needs from the user (<uses-permission>).
  • Minimum and target SDK versions (via <uses-sdk>, though these days they are typically set in Gradle).
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.MyApp"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
android:exported must be set explicitly on API 31+. Starting with Android 12 (API level 31), activities that declare an <intent-filter> must set android:exported="true" or "false". Omitting it causes the build to fail. The launcher activity is always exported="true"; internal activities that only your app starts should be exported="false".

The Gradle Build System

Android uses Gradle as its build tool. There are two build scripts you must understand: the project-level script and the module-level script.

Project-level build.gradle — declares which Gradle plugins are available to all modules. You rarely edit this file beyond adding a new plugin to the classpath.

// Top-level build.gradle (project scope) plugins { id 'com.android.application' version '8.3.0' apply false id 'org.jetbrains.kotlin.android' version '1.9.23' apply false }

Module-level app/build.gradle — this is where you spend most of your time. It controls the Android SDK versions, signing config, and every library dependency your app uses.

// app/build.gradle plugins { id 'com.android.application' } android { namespace 'com.example.myapp' compileSdk 34 defaultConfig { applicationId "com.example.myapp" minSdk 24 // minimum Android version you support (Android 7.0) targetSdk 34 // SDK your app is built and tested against versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } dependencies { implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' }

Key settings to understand:

  • compileSdk — the SDK version the compiler uses. Set to the latest stable release so you can use new APIs at compile time (with runtime checks for older devices).
  • minSdk — the oldest Android version your app will run on. Lowering it increases your potential audience but restricts the APIs you can call unconditionally.
  • targetSdk — tells Android which behaviour model your app expects. As you raise it you opt into new system behaviours (e.g. scoped storage, notification permissions). Google Play requires apps to target a recent SDK.
  • applicationId — the unique identifier for your app on the Play Store. Once published, this cannot change.
Dependency scopes: implementation means the library is available to your module but not exposed to other modules that depend on yours — the right choice for almost every dependency. testImplementation adds a library only for JVM unit tests; androidTestImplementation only for instrumented (on-device) tests. Keep test libraries out of the main classpath.

settings.gradle

The settings.gradle file at the project root tells Gradle which modules exist. For a single-module app it simply contains:

pluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { google() mavenCentral() } } rootProject.name = "MyAndroidApp" include ':app'

When you add a library module or a feature module to your project, you add another include ':moduleName' line here and create the corresponding directory.

Summary

The Android project structure is a deliberate separation of concerns: Java sources compile your logic, res/ externalises everything that varies by locale or screen density, AndroidManifest.xml registers your components with the OS, and the Gradle scripts wire it all together into an APK or AAB. In the next lesson you will create an Activity and attach a layout, putting these folders to direct use.