Saat pertama kali membuka proyek Android baru, tumpukan folder dan file yang muncul dapat membingungkan. Ada app/, gradle/, settings.gradle.kts, dua file build.gradle.kts (root dan modul), AndroidManifest.xml, serta folder main, test, dan androidTest. Pada chapter ini kita akan membongkar struktur tersebut satu per satu supaya tidak lagi sekadar folder tidak jelasus, melainkan peta kerja yang kita pahami fungsinya.

Modul Gradle dan settings.gradle.kts #

Proyek Android pada dasarnya adalah proyek Gradle multi-modul. File settings.gradle.kts di root mendaftarkan modul-modul yang termasuk dalam build, biasanya :app untuk aplikasi utama dan :core-data atau :feature-home untuk modul library. Setiap modul memiliki build.gradle.kts sendiri yang menerapkan plugin yang sesuai: com.android.application untuk modul APK, com.android.library untuk modul AAR, dan org.jetbrains.kotlin.android untuk kedua jenis modul Kotlin.

Konfigurasi modul aplikasi minimal terlihat seperti berikut:

kotlin app/build.gradle.kts (ringkas)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.tugasapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.tugasapp"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
}

Field namespace menentukan paket R class yang dihasilkan (mis. com.example.tugasapp.R), sedangkan applicationId adalah identifier unik aplikasi di Play Store. Keduanya boleh berbeda — aplikasi yang di-flavoring sering memakai applicationIdSuffix per variant.

Struktur Source Set #

Source set adalah cara Gradle memisahkan kode berdasarkan tujuan dan lingkungan. Android menyediakan tiga source set default:

Source setLokasiTujuan
mainsrc/main/kotlinKode produksi, dipakai semua build variant
testsrc/test/kotlinUnit test JVM murni (tidak butuh Android)
androidTestsrc/androidTest/kotlinInstrumented test yang berjalan di peranti/emulator

Pemisahan ini bukan sekadar konvensi folder, melainkan kontrak Gradle. Method testImplementation hanya menyediakan dependency untuk source set test, sedangkan androidTestImplementation hanya untuk androidTest. Dependency berat seperti Robolectric hanya muncul saat menjalankan unit test, tidak ikut di-bundle ke APK rilis.

AndroidManifest.xml #

File AndroidManifest.xml adalah peta jalan sistem operasi terhadap aplikasi kita. Sistem membaca manifest untuk mengetahui paket aplikasi, daftar Activity yang boleh dijalankan, Service yang dapat diikat, dan permission yang diminta. Setiap Activity yang ingin dapat diluncurkan dari launcher harus memiliki <intent-filter> dengan action MAIN dan kategori LAUNCHER.

xml AndroidManifest.xml (ringkas)
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:label="Tugas App"
        android:icon="@mipmap/ic_launcher">
        <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>
        <activity android:name=".TugasDetailActivity" />
    </application>
</manifest>

Manifest tidak boleh menyebut Activity yang tidak ada di kode. Begitu pula sebaliknya — Activity tanpa deklarasi di manifest akan mengakibatkan ActivityNotFoundException saat dipanggil.

Simulasi Struktur sebagai Data #

Cuplikan berikut memetakan struktur di atas menjadi objek Kotlin yang dapat dijalankan tanpa Android SDK:

kotlin
// android_project_structure.kt
// Mensimulasikan struktur modul Gradle, source set, dan manifest sebagai
// struktur data ringkas yang dapat dijalankan di JVM murni.

data class SourceSet(val nama: String, val path: String, val peran: String)
data class ModulApp(val namespace: String, val minSdk: Int, val targetSdk: Int)

class StrukturProyek(val modulUtama: ModulApp) {
    private val sourceSets = mutableListOf<SourceSet>()

    init {
        sourceSets.add(SourceSet("main", "src/main", "Kode produksi + AndroidManifest"))
        sourceSets.add(SourceSet("test", "src/test", "Unit test JVM (tanpa Android framework)"))
        sourceSets.add(SourceSet("androidTest", "src/androidTest", "Instrumented test di peranti"))
    }

    fun daftarSourceSet(): List<SourceSet> = sourceSets.toList()
    fun konfigurasiBuild(): String =
        "namespace=${modulUtama.namespace} minSdk=${modulUtama.minSdk} targetSdk=${modulUtama.targetSdk}"
}

data class KontrakManifest(
    val packageName: String,
    val activities: List<String>,
    val permissions: List<String>
) {
    fun ringkas(): List<String> = buildList {
        add("package=$packageName")
        activities.forEach { add("activity=$it") }
        permissions.forEach { add("uses-permission=$it") }
    }
}

fun main() {
    val app = ModulApp(namespace = "com.example.tugasapp", minSdk = 24, targetSdk = 34)
    val proyek = StrukturProyek(app)
    println("Modul: ${proyek.konfigurasiBuild()}")
    println("Source sets:")
    proyek.daftarSourceSet().forEach { println("  ${it.nama} (${it.path}) -> ${it.peran}") }

    val manifest = KontrakManifest(
        packageName = "com.example.tugasapp",
        activities = listOf(".MainActivity", ".TugasDetailActivity"),
        permissions = listOf("android.permission.INTERNET", "android.permission.POST_NOTIFICATIONS")
    )
    println("Manifest:")
    manifest.ringkas().forEach { println("  $it") }
}

Output yang Dihasilkan:

text
Modul: namespace=com.example.tugasapp minSdk=24 targetSdk=34
Source sets:
  main (src/main) -> Kode produksi + AndroidManifest
  test (src/test) -> Unit test JVM (tanpa Android framework)
  androidTest (src/androidTest) -> Instrumented test di peranti
Manifest:
  package=com.example.tugasapp
  activity=.MainActivity
  activity=.TugasDetailActivity
  uses-permission=android.permission.INTERNET
  uses-permission=android.permission.POST_NOTIFICATIONS

Struktur data ini sederhana namun mencerminkan keputusan nyata di Gradle: setiap source set memiliki path dan peran yang berbeda, dan modul utama memiliki kontrak berupa namespace, minSdk, serta targetSdk.

Referensi Lanjutan #

  • Guide to app architecture — panduan resmi arsitektur aplikasi Android yang menjelaskan struktur modul dan layer UI
  • Jetpack — rujukan Jetpack sebagai dasar library yang dipakai pada struktur proyek Android

Selanjutnya #

Setelah peta struktur proyek terbentuk, kita akan masuk ke konsep paling khas Android: Activity lifecycle. Kita akan membedah state created, started, resumed, paused, stopped, dan destroyed beserta callback yang menyertainya pada chapter Activity Lifecycle.