Skip to main content

Android Platform

Build and deploy GreenhouseAdmin as a native Android application. This guide covers development setup, building APKs, and Android-specific configuration.

Overview

The Android platform uses:
  • Target SDK: 36 (Android 15)
  • Minimum SDK: 24 (Android 7.0 Nougat)
  • Compile SDK: 36
  • JVM Target: Java 11
  • HTTP Engine: OkHttp (Ktor client)
The minimum SDK 24 ensures compatibility with 95%+ of Android devices in the market.

Prerequisites

1

Install Android SDK

Install Android Studio or Android command-line tools:Required SDK components:
  • Android SDK Platform 36
  • Android SDK Build-Tools 34.0.0+
  • Android SDK Platform-Tools
2

Configure ANDROID_HOME

Set the ANDROID_HOME environment variable:
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools
3

Verify Installation

Verify Android tools are accessible:
adb --version

Configuration

Android-Specific Dependencies

The Android platform uses the OkHttp engine for Ktor networking:
composeApp/build.gradle.kts
androidMain.dependencies {
    implementation(compose.preview)
    implementation(libs.androidx.activity.compose)
    implementation(libs.koin.android)
    
    // Ktor - OkHttp engine for Android
    implementation(libs.ktor.client.okhttp)
}

Application Configuration

composeApp/build.gradle.kts
android {
    namespace = "com.apptolast.greenhouse.admin"
    compileSdk = 36

    defaultConfig {
        applicationId = "com.apptolast.greenhouse.admin"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }
    
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}

Dependency Injection (Koin)

Android requires platform-specific Koin initialization in the Application class:
androidMain/.../GreenhouseApplication.kt
class GreenhouseApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        initKoin {
            androidContext(this@GreenhouseApplication)
        }
    }
}
Register in AndroidManifest.xml:
<application
    android:name=".GreenhouseApplication"
    android:label="@string/app_name"
    ...>

Development

Running on Emulator/Device

1

Open Project

Open the project root directory in Android Studio.
2

Select Configuration

Choose composeApp from the run configuration dropdown in the toolbar.
3

Select Device

Choose a connected device or emulator from the device dropdown.
4

Run

Click the Run button (green triangle) or press Shift+F10.

Viewing Logs

Monitor application logs with Logcat:
adb logcat | grep GreenhouseAdmin
Or filter by tag:
adb logcat -s "ktor.client"

Building APKs

Debug Build

Build a debug APK for development:
./gradlew :composeApp:assembleDebug
Output: composeApp/build/outputs/apk/debug/composeApp-debug.apk

Release Build

Build a release APK for production:
1

Create Signing Key

Generate a keystore for signing:
keytool -genkey -v -keystore greenhouse-release.keystore \
  -alias greenhouse -keyalg RSA -keysize 2048 -validity 10000
Store the keystore and passwords securely. You’ll need them for all future releases.
2

Configure Signing

Create keystore.properties in the project root:
keystore.properties
storeFile=/path/to/greenhouse-release.keystore
storePassword=YOUR_STORE_PASSWORD
keyAlias=greenhouse
keyPassword=YOUR_KEY_PASSWORD
Add to .gitignore:
keystore.properties
*.keystore
3

Update Build Configuration

Add signing config to build.gradle.kts:
composeApp/build.gradle.kts
android {
    signingConfigs {
        create("release") {
            val keystoreProperties = Properties()
            keystoreProperties.load(FileInputStream(rootProject.file("keystore.properties")))
            
            storeFile = file(keystoreProperties["storeFile"] as String)
            storePassword = keystoreProperties["storePassword"] as String
            keyAlias = keystoreProperties["keyAlias"] as String
            keyPassword = keystoreProperties["keyPassword"] as String
        }
    }
    
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
            signingConfig = signingConfigs.getByName("release")
        }
    }
}
4

Build Release APK

Build the signed release APK:
./gradlew :composeApp:assembleRelease
Output: composeApp/build/outputs/apk/release/composeApp-release.apk

Android App Bundle (AAB)

Build an Android App Bundle for Google Play Store distribution:
./gradlew :composeApp:bundleRelease
Output: composeApp/build/outputs/bundle/release/composeApp-release.aab
Google Play Store requires AAB format for new app submissions.

Android-Specific Features

Activity Compose Integration

The Android app uses Jetpack Compose with activity-compose:
androidMain/.../MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        setContent {
            App()  // Shared Compose UI from commonMain
        }
    }
}

Permissions

For network access, declare in AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

ProGuard/R8

For release builds with code shrinking, create proguard-rules.pro:
composeApp/proguard-rules.pro
# Ktor
-keep class io.ktor.** { *; }
-keepclassmembers class io.ktor.** { *; }

# Kotlinx Serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.**
-keep,includedescriptorclasses class com.apptolast.greenhouse.admin.**$$serializer { *; }
-keepclassmembers class com.apptolast.greenhouse.admin.** {
    *** Companion;
}

# Koin
-keep class org.koin.** { *; }
-keep class kotlin.Metadata { *; }

Testing

Emulator Setup

Create an emulator for testing:
# List available system images
avdmanager list

# Create emulator
avdmanager create avd -n Pixel_7_API_36 \
  -k "system-images;android-36;google_apis;x86_64" \
  -d pixel_7

# Start emulator
emulator -avd Pixel_7_API_36

Device Testing

Connect a physical device:
1

Enable Developer Options

On the device:
  1. Go to Settings > About Phone
  2. Tap Build Number 7 times
2

Enable USB Debugging

  1. Go to Settings > Developer Options
  2. Enable USB Debugging
3

Connect via USB

Connect the device and authorize the computer:
adb devices
Accept the authorization prompt on the device.

Troubleshooting

Error: SDK location not foundSolution: Set the ANDROID_HOME environment variable or create local.properties:
sdk.dir=/path/to/Android/Sdk
Error: No devices shown in adb devicesSolution:
  1. Enable USB debugging on device
  2. Try different USB cable/port
  3. Restart adb server:
    adb kill-server
    adb start-server
    
Error: Cannot fit requested classes in a single dex fileSolution: Enable multidex in build.gradle.kts:
android {
    defaultConfig {
        multiDexEnabled = true
    }
}
Error: SSLHandshakeExceptionSolution: Ensure your API uses a valid SSL certificate. For development with self-signed certificates, you may need to add network security configuration.

Next Steps

iOS Platform

Build and deploy to iOS devices

Web Platform

Deploy as a web application

Architecture

Understand MVVM architecture and DI

Building

Build for all platforms

Build docs developers (and LLMs) love