Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
📱 Android App (Kotlin + XML)  ·  Lesson 13

App Setup: Gradle, Packages और MVVM

Step 1: Project बनाएँ या खोलें

इन lessons को follow करके हर file खुद बना सकते हैं, या download से बना-बनाया project खोलकर साथ में पढ़ सकते हैं। नए से शुरू करने के लिए:

  1. 1Android Studio → New Project → Empty Views Activity (यह template XML layouts use करता है)
  2. 2Name KartEasy, package com.codingeasily.karteasy, language Kotlin, minimum SDK API 26, build language Kotlin DSL
  3. 3Generated Gradle files को नीचे वाली files से बदलें और Sync Now दबाएँ
  4. 4या: File → Open → ZIP का android/KartEasy folder चुनें
New project settings
🔒 Android Studio – New ProjectName: KartEasyPackage name: com.codingeasily.karteasyLanguage: KotlinMinimum SDK: API 26 ("Oreo"; Android 8.0)Build configuration language: Kotlin DSLFinish

Step 2: Version catalog और dependencies

सारे library versions एक file में रहते हैं, gradle/libs.versions.toml। Module file उन्हें libs.retrofit, libs.glide आदि नाम से use करती है।

TOML – gradle/libs.versions.toml
[versions]
agp = "8.7.3"
kotlin = "2.0.21"
coreKtx = "1.15.0"
appcompat = "1.7.0"
material = "1.12.0"
constraintlayout = "2.2.0"
recyclerview = "1.4.0"
viewpager2 = "1.1.0"
swiperefresh = "1.1.0"
activityKtx = "1.9.3"
fragmentKtx = "1.8.5"
lifecycle = "2.8.7"
navigation = "2.8.5"
datastore = "1.1.1"
splashscreen = "1.0.1"
coroutines = "1.9.0"
retrofit = "2.11.0"
okhttp = "4.12.0"
glide = "4.16.0"
junit = "4.13.2"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "splashscreen" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" }
androidx-viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" }
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefresh" }
androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" }
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" }
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
junit = { group = "junit", name = "junit", version.ref = "junit" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
Libraryकिसके लिए
material, constraintlayout, recyclerview, viewpager2, swiperefreshlayoutUI: cards, chips, grids, banner slider, pull to refresh
lifecycle, activity-ktx, fragment-ktxViewModel, by viewModels(), lifecycle-aware collection
navigationscreens के बीच जाना, back stack, deep links, bottom nav + drawer
retrofit + converter-gson + okhttp-loggingPHP API call करना, JSON → Kotlin, logs, offline cache
glideURLs से product images load करना
datastore-preferenceslogin token और settings save करना
core-splashscreenहर version पर Android 12+ style splash screen
Kotlin DSL – build.gradle.kts (project)
// Top-level build file: only declares plugins, the app module applies them.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
}
Kotlin DSL – settings.gradle.kts
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "KartEasy"
include(":app")
Kotlin DSL – app/build.gradle.kts
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
}

android {
    namespace = "com.codingeasily.karteasy"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.codingeasily.karteasy"
        minSdk = 26
        targetSdk = 35
        versionCode = 1
        versionName = "1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    // Upload key for the Play Store. Create it once with Build > Generate Signed Bundle,
    // then put the 4 values in ~/.gradle/gradle.properties (never commit passwords).
    signingConfigs {
        create("release") {
            val storeFilePath = providers.gradleProperty("KARTEASY_STORE_FILE").orNull
            if (storeFilePath != null) {
                storeFile = file(storeFilePath)
                storePassword = providers.gradleProperty("KARTEASY_STORE_PASSWORD").orNull
                keyAlias = providers.gradleProperty("KARTEASY_KEY_ALIAS").orNull
                keyPassword = providers.gradleProperty("KARTEASY_KEY_PASSWORD").orNull
            }
        }
    }

    buildTypes {
        debug {
            // Emulator -> your computer running "php -S 0.0.0.0:8000" inside the backend folder
            buildConfigField("String", "BASE_URL", "\"http://10.0.2.2:8000/\"")
            applicationIdSuffix = ".debug"
            resValue("string", "app_name", "KartEasy Debug")
        }
        release {
            // Your live backend (must end with a slash)
            buildConfigField("String", "BASE_URL", "\"https://yourdomain.com/karteasy/\"")
            resValue("string", "app_name", "KartEasy")
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
            if (providers.gradleProperty("KARTEASY_STORE_FILE").isPresent) {
                signingConfig = signingConfigs.getByName("release")
            }
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
    buildFeatures {
        viewBinding = true
        buildConfig = true
    }
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.core.splashscreen)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    implementation(libs.androidx.constraintlayout)
    implementation(libs.androidx.recyclerview)
    implementation(libs.androidx.viewpager2)
    implementation(libs.androidx.swiperefreshlayout)
    implementation(libs.androidx.activity.ktx)
    implementation(libs.androidx.fragment.ktx)
    implementation(libs.androidx.lifecycle.viewmodel.ktx)
    implementation(libs.androidx.lifecycle.livedata.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.navigation.fragment.ktx)
    implementation(libs.androidx.navigation.ui.ktx)
    implementation(libs.androidx.datastore.preferences)
    implementation(libs.kotlinx.coroutines.android)
    implementation(libs.retrofit)
    implementation(libs.retrofit.converter.gson)
    implementation(libs.okhttp.logging)
    implementation(libs.glide)
    testImplementation(libs.junit)
}

Step 3: Debug और release API URL

buildConfigField lines BuildConfig.BASE_URL बनाती हैं। Debug builds आपके computer से बात करते हैं, release builds live server से — switch करने के लिए code नहीं बदलना पड़ता।

debug (Android Studio में Run ▶)

  • BASE_URL = http://10.0.2.2:8000/
  • app name "KartEasy Debug"
  • package …karteasy.debug (दोनों install हो सकते हैं)
  • HTTP logging ON

release (Play Store)

  • BASE_URL = https://yourdomain.com/karteasy/
  • app name "KartEasy"
  • R8 code shrink और obfuscate करता है
  • HTTP logging OFF
एक project, दो configurations
📌 10.0.2.2 क्या है?

Android emulator के अंदर localhost खुद emulator है। Special address 10.0.2.2 का मतलब है "वह computer जिस पर emulator चल रहा है", जहाँ आपका php -S server सुन रहा है।

Step 4: Manifest और network security

XML – app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />
    <!-- NetworkMonitor checks if the phone is online -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- Android 13+ asks before showing the "Order placed" notification -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:name=".KartApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:supportsRtl="true"
        android:theme="@style/Theme.KartEasy">

        <!-- Launcher: shows the splash screen, then decides where to go -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/Theme.KartEasy.Splash"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <!-- Creates intent filters for the <deepLink> entries in nav_main.xml -->
            <nav-graph android:value="@navigation/nav_main" />
        </activity>

        <activity
            android:name=".ui.onboarding.OnboardingActivity"
            android:exported="false" />

        <activity
            android:name=".ui.auth.AuthActivity"
            android:exported="false"
            android:windowSoftInputMode="adjustResize" />
    </application>
</manifest>

Android default रूप से plain http:// block करता है। हम इसे सिर्फ development addresses के लिए allow करते हैं, ताकि live app को HTTPS ही use करना पड़े:

XML – res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- HTTPS everywhere. Plain HTTP is allowed only for your own computer while developing
     (10.0.2.2 = the computer, seen from the Android emulator). -->
<network-security-config>
    <base-config cleartextTrafficPermitted="false" />
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="false">10.0.2.2</domain>
        <domain includeSubdomains="false">localhost</domain>
    </domain-config>
</network-security-config>

Step 5: Package structure

📁 app/src/main/java/com/codingeasily/karteasy/
  KartApp.kt                Application: creates shared objects once
  MainActivity.kt           splash, toolbar, drawer, bottom nav, badge
  📁 data/
    📁 api/      ApiService.kt  ApiClient.kt  Result.kt
    📁 model/    Models.kt                     (all JSON classes)
    📁 repo/     ShopRepository.kt             (only class that calls the API)
    📁 session/  SessionManager.kt             (DataStore: token, theme …)
  📁 notify/     Notifier.kt
  📁 util/       Format.kt  Validator.kt  NetworkMonitor.kt  Extensions.kt
  📁 ui/
    📁 onboarding/  auth/  home/  category/  product/
    📁 cart/  wishlist/  checkout/  address/  orders/  account/

📁 app/src/main/res/
  📁 layout/ (35 files)  drawable/ (47)  navigation/ nav_main, nav_auth
  📁 menu/  values/  values-night/  values-sw600dp/  xml/  anim/  color/
Layer (data, util) और screen (ui/…) के हिसाब से organise करें

MVVM: कौन क्या करता है

FragmentViewModelShopRepositoryPHP APIvm.addToCart(7)repo.addToCart(7)POST cart/add.phpJSONResult.Success(cart)StateFlow नई state emit करता हैviews update
Click नीचे जाता है, data ऊपर लौटता है
Layerकिसके बारे में जानती हैकभी नहीं करती
Fragment + XMLviews, clicks, ViewModelnetwork calls, business rules
ViewModelscreen state, repositoryviews छूना (ताकि rotation में बची रहे)
RepositoryApiService, SessionManagerscreens के बारे में कुछ भी

Step 6: KartApp (shared objects)

Application class किसी भी screen से पहले बनती है और app जितनी देर चलती है उतनी देर रहती है। हम session, network monitor और repository वहाँ एक बार बनाते हैं, और हर screen उन्हें reuse करती है।

Kotlin – KartApp.kt
package com.codingeasily.karteasy

import android.app.Application
import com.codingeasily.karteasy.data.api.ApiClient
import com.codingeasily.karteasy.data.repo.ShopRepository
import com.codingeasily.karteasy.data.session.SessionManager
import com.codingeasily.karteasy.notify.Notifier
import com.codingeasily.karteasy.util.NetworkMonitor

/**
 * Application class = simple "service locator".
 * Objects created here live as long as the app and are shared by every screen:
 * (application as KartApp).repository
 */
class KartApp : Application() {

    lateinit var session: SessionManager
        private set
    lateinit var network: NetworkMonitor
        private set
    lateinit var repository: ShopRepository
        private set

    override fun onCreate() {
        super.onCreate()
        session = SessionManager(this)
        network = NetworkMonitor(this)
        repository = ShopRepository(ApiClient.create(this, session, network), session)
        Notifier.createChannels(this)
    }
}
Kotlin – use करना
// Anywhere in a Fragment or ViewModel:
val repo = requireContext().kartApp.repository      // Extensions.kt: Context.kartApp

// In an AndroidViewModel:
class HomeViewModel(app: Application) : AndroidViewModel(app) {
    private val repo = app.kartApp.repository
}

Step 7: App + backend साथ चलाएँ

Terminal 1cd backend && php -S 0.0.0.0:8000Browser checklocalhost:8000/api/ → connectedAndroid Studioemulator पर Run ▶App10.0.2.2:8000 call करती है
पहले server, फिर app
📱 असली phone पर testing

Phone को उसी Wi-Fi पर रखें, अपने PC का IP निकालें (ipconfig / ifconfig, जैसे 192.168.1.5), debug BASE_URL को http://192.168.1.5:8000/ करें और वह IP network_security_config.xml में जोड़ें।

अक्सर पूछे जाने वाले प्रश्न (FAQ)

Gradle sync error दे रहा है — क्या check करूँ?
Android Studio Ladybug या नया JDK 17 (bundled default) के साथ use करें, पहली sync के लिए internet रखें, और पूछे तो SDK 35 download accept करें। कोई library update हुई हो तो libs.versions.toml में Android Studio का "Update to" suggestion accept करना safe है।
Hilt या Dagger क्यों नहीं?
इस app के लिए एक छोटी Application class काफ़ी है जो repository एक बार बनाए, और पढ़ने में आसान है। Project बड़ा होने पर Hilt, ViewModels ज़्यादा बदले बिना KartApp की जगह ले सकता है।
minSdk 26 क्यों?
Android 8.0+ आज India में use हो रहे लगभग सारे phones cover करता है, और notification channels व adaptive icons बिना extra checks के use कर सकते हैं।
← E-commerce App Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicसुंदर Theme: Colors, Fonts और Styles आपकी प्रगति: 13 / 28 (46%) · Lesson 14 / 28 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें