Android Development Overview
Modern Android apps require secure local storage, biometric authentication, offline-first architecture with Room, and strict compliance with security standards. Android development with Jetpack Compose and Kotlin Coroutines enables robust, maintainable apps. Poor mobile practices lead to memory leaks, ANRs (Application Not Responding), security vulnerabilities, and poor Google Play ratings. Target Play Store rating: 4.5+, crash-free rate >99.5%.
Overview
This guide provides a comprehensive overview of Android development best practices. We cover project setup, Gradle configuration, recommended versions, project structure following Clean Architecture principles (see Architecture Overview for foundational concepts), development workflow, dependency injection with Hilt, and core architectural principles.
What This Guide Covers
- Project Setup: Android Studio, Gradle, Kotlin configuration
- Project Structure: Clean Architecture folder organization for Android
- Development Workflow: From development to Google Play submission
- Dependency Injection: Hilt for testability and maintainability
- Code Review Checklist: Common mistakes and best practices
Related Android Guides
This overview sets the foundation. For specialized topics, see:
- Android UI Development - Jetpack Compose, Material Design 3, navigation
- Android Data & Networking - Retrofit, Room, repositories
- Android Security - EncryptedSharedPreferences, biometrics, certificate pinning
- Android Architecture - MVVM, Clean Architecture, patterns
- Android Testing - JUnit, Espresso, UI testing
- Android Performance - Profiling, optimization, memory management
Core Principles
1. Kotlin First
Modern Kotlin idioms, null safety, coroutines. Avoid Java legacy patterns.
2. Jetpack Compose
Declarative UI with Material Design 3. Compose is the standard for new Android development.
3. MVVM Architecture
Clear separation between UI and business logic. ViewModels survive configuration changes.
4. Dependency Injection with Hilt
Dagger-based DI for compile-time safety and Android-optimized dependency graphs.
5. Offline-First
Room database with local caching and sync strategies. Banking apps must work offline.
6. Security
EncryptedSharedPreferences, Keystore, biometric authentication, certificate pinning.
7. Performance
Lazy loading, efficient LazyColumn, memory management, and avoiding ANRs.
8. Testability
Design for testability from the start. Use dependency injection, repository pattern, and ViewModels.
Android Development Lifecycle
Project Setup
Recommended Versions
// build.gradle (Project level)
buildscript {
ext {
kotlin_version = '1.9.20'
compose_version = '1.5.4'
hilt_version = '2.48'
}
}
plugins {
id 'com.android.application' version '8.1.4' apply false
id 'org.jetbrains.kotlin.android' version '1.9.20' apply false
id 'com.google.dagger.hilt.android' version '2.48' apply false
}
Module build.gradle
// build.gradle (Module :app)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
namespace 'com.bank.paymentapp'
compileSdk 34
defaultConfig {
applicationId "com.bank.paymentapp"
minSdk 26 // Android 8.0 (95%+ of users)
targetSdk 34
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "String", "API_BASE_URL", '"https://api.bank.com"'
}
debug {
buildConfigField "String", "API_BASE_URL", '"https://api-dev.bank.com"'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
freeCompilerArgs += [
'-opt-in=kotlin.RequiresOptIn',
'-Xjvm-default=all'
]
}
buildFeatures {
compose true
buildConfig true
}
composeOptions {
kotlinCompilerExtensionVersion = '1.5.4'
}
packaging {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
// Core Android
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation 'androidx.activity:activity-compose:1.8.1'
// Compose
implementation platform('androidx.compose:compose-bom:2023.10.01')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.navigation:navigation-compose:2.7.5'
// Hilt Dependency Injection
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
implementation 'androidx.hilt:hilt-navigation-compose:1.1.0'
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
// Room Database
implementation 'androidx.room:room-runtime:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
kapt 'androidx.room:room-compiler:2.6.1'
// Retrofit for API calls
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
// Security
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
implementation 'androidx.biometric:biometric:1.1.0'
// Testing
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'app.cash.turbine:turbine:1.0.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation platform('androidx.compose:compose-bom:2023.10.01')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
}
Apps should support minSdk 26 (Android 8.0) to reach 95%+ of users. Android 8.0 introduced background execution limits, notification channels, and improved permission handling. Lower API levels lack modern security capabilities and are increasingly unsupported by third-party libraries. Monitor Android version distribution at Android Studio Distribution Dashboard to make informed decisions about minimum supported versions.
Project Structure
Clean Architecture Directory Organization
app/
├── src/
│ ├── main/
│ │ ├── java/com/bank/paymentapp/
│ │ │ ├── data/ # Data layer
│ │ │ │ ├── local/ # Room database
│ │ │ │ │ ├── dao/
│ │ │ │ │ │ ├── PaymentDao.kt
│ │ │ │ │ │ └── AccountDao.kt
│ │ │ │ │ ├── entities/
│ │ │ │ │ │ ├── PaymentEntity.kt
│ │ │ │ │ │ └── AccountEntity.kt
│ │ │ │ │ └── PaymentDatabase.kt
│ │ │ │ ├── remote/ # Retrofit API
│ │ │ │ │ ├── api/
│ │ │ │ │ │ ├── PaymentApi.kt
│ │ │ │ │ │ └── AuthApi.kt
│ │ │ │ │ └── dto/
│ │ │ │ │ ├── PaymentDto.kt
│ │ │ │ │ └── AccountDto.kt
│ │ │ │ └── repository/ # Repository implementations
│ │ │ │ ├── PaymentRepositoryImpl.kt
│ │ │ │ └── AccountRepositoryImpl.kt
│ │ │ ├── domain/ # Domain layer (framework-independent)
│ │ │ │ ├── model/ # Domain models
│ │ │ │ │ ├── Payment.kt
│ │ │ │ │ └── Account.kt
│ │ │ │ ├── repository/ # Repository interfaces
│ │ │ │ │ ├── PaymentRepository.kt
│ │ │ │ │ └── AccountRepository.kt
│ │ │ │ └── usecase/ # Use cases
│ │ │ │ ├── GetPaymentsUseCase.kt
│ │ │ │ └── CreatePaymentUseCase.kt
│ │ │ ├── presentation/ # UI layer
│ │ │ │ ├── navigation/
│ │ │ │ │ ├── NavGraph.kt
│ │ │ │ │ └── Screen.kt
│ │ │ │ ├── screens/
│ │ │ │ │ ├── payments/
│ │ │ │ │ │ ├── PaymentListScreen.kt
│ │ │ │ │ │ ├── PaymentListViewModel.kt
│ │ │ │ │ │ ├── PaymentDetailScreen.kt
│ │ │ │ │ │ └── PaymentDetailViewModel.kt
│ │ │ │ │ ├── profile/
│ │ │ │ │ │ ├── ProfileScreen.kt
│ │ │ │ │ │ └── ProfileViewModel.kt
│ │ │ │ │ └── auth/
│ │ │ │ │ ├── LoginScreen.kt
│ │ │ │ │ └── LoginViewModel.kt
│ │ │ │ ├── components/ # Reusable UI components
│ │ │ │ │ ├── PaymentCard.kt
│ │ │ │ │ ├── LoadingIndicator.kt
│ │ │ │ │ └── ErrorView.kt
│ │ │ │ └── theme/ # Material Design theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Type.kt
│ │ │ │ └── Theme.kt
│ │ │ ├── di/ # Hilt modules
│ │ │ │ ├── AppModule.kt
│ │ │ │ ├── NetworkModule.kt
│ │ │ │ ├── DatabaseModule.kt
│ │ │ │ └── RepositoryModule.kt
│ │ │ └── PaymentApplication.kt # Application class
│ │ ├── res/ # Resources
│ │ │ ├── values/
│ │ │ ├── drawable/
│ │ │ └── mipmap/
│ │ └── AndroidManifest.xml
│ ├── test/ # Unit tests
│ │ └── java/com/bank/paymentapp/
│ └── androidTest/ # Instrumented tests
│ └── java/com/bank/paymentapp/
├── build.gradle
└── proguard-rules.pro
Architecture Layers Diagram
Dependency Flow Diagram
Domain layer has ZERO dependencies on Android framework or other layers. This is critical for testability and business logic isolation. Data and Presentation layers depend on Domain through interfaces (Dependency Inversion Principle).
Dependency Injection with Hilt
Hilt Setup
// PaymentApplication.kt
package com.bank.paymentapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class PaymentApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize Firebase, Crashlytics, etc.
}
}
<!-- AndroidManifest.xml -->
<application
android:name=".PaymentApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.BankingApp">
<!-- Activities -->
</application>
Hilt Modules
// di/NetworkModule.kt
package com.bank.paymentapp.di
import com.bank.paymentapp.data.remote.api.PaymentApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.bank.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun providePaymentApi(retrofit: Retrofit): PaymentApi {
return retrofit.create(PaymentApi::class.java)
}
}
Hilt Dependency Graph
Development Workflow
Feature Development Flow
Build Configuration
Build Variants
Common Mistakes
Don't: Use GlobalScope for Coroutines
// BAD: GlobalScope survives application lifecycle
GlobalScope.launch {
val payments = repository.getPayments()
}
// GOOD: Use ViewModelScope tied to ViewModel lifecycle
viewModelScope.launch {
val payments = repository.getPayments()
}
Don't: Store Secrets in SharedPreferences
// BAD: SharedPreferences is NOT encrypted
val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
prefs.edit().putString("auth_token", token).apply()
// GOOD: Use EncryptedSharedPreferences
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedPrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptedPrefs.edit().putString("auth_token", token).apply()
Don't: Block Main Thread
// BAD: Blocking main thread causes ANR
fun loadPayments() {
val payments = runBlocking { // BLOCKS UI!
repository.getPayments()
}
}
// GOOD: Use coroutines in ViewModel
fun loadPayments() {
viewModelScope.launch {
_state.value = PaymentState.Loading
try {
val payments = repository.getPayments()
_state.value = PaymentState.Success(payments)
} catch (e: Exception) {
_state.value = PaymentState.Error(e.message)
}
}
}
Code Review Checklist
Security Red Flags (Block PR)
- No secrets in SharedPreferences - Use EncryptedSharedPreferences
- No hardcoded API keys - Use BuildConfig or secure storage
- Certificate pinning implemented - For production API calls
- No sensitive data logged - Account numbers, tokens, PINs
- Biometric auth has fallback - Support PIN entry
- ProGuard/R8 enabled - For release builds
Watch For (Request Changes)
- No GlobalScope coroutines - Use viewModelScope or lifecycleScope
- Main thread not blocked - Network/DB operations in coroutines
- Memory leaks prevented - No activity/context leaks in ViewModels
- Null safety - Proper null checks or safe calls
- Large composables (300+ lines) - Break into smaller functions
- Missing error handling - All coroutines have try/catch
- Deprecated APIs - Check for Android deprecation warnings
Best Practices (Approve if Present)
- Dependency injection used - Hilt for all dependencies
- Repository pattern - Abstracts data sources
- Unit tests included - ViewModels and use cases tested
- String resources - No hardcoded user-facing text
- Compose previews - All composables have @Preview
- Proper coroutine usage - Structured concurrency
- State hoisting - Stateless composables where possible
Additional Checks
- BigDecimal for currency - Never Float/Double for monetary values (precision loss)
- Audit logging - Critical user actions logged with user ID, timestamp, action type
- Offline support - Core features work without network via Room caching
- Input validation - All user inputs validated (format, range, type)
- Accessibility - Content descriptions for all interactive elements
Performance Considerations
App Launch Time
Target: Cold start < 2 seconds, warm start < 0.5 seconds
Further Reading
Android Framework Guidelines
- Android UI Development - Jetpack Compose and Material Design 3
- Android Data & Networking - Retrofit, Room, repositories
- Android Security - Encryption, biometrics, secure storage
- Android Architecture - MVVM, Clean Architecture, navigation
- Android Testing - JUnit, Espresso, testing strategies
- Android Performance - Profiling, optimization, memory management
Language and Cross-Platform Guidelines
- Kotlin Best Practices - Kotlin language guidelines and idioms
- Kotlin Concurrency - Coroutines and Flow patterns
- Mobile Overview - Cross-platform mobile guidance
- Mobile Navigation - Navigation patterns
- Architecture Overview - Clean Architecture and design principles
- Security Overview - Cross-platform security principles
- API Overview - RESTful API principles
- OpenAPI Frontend Integration - API client generation
External Resources
- Android Developer Guides
- Jetpack Compose Documentation
- Material Design 3
- Kotlin Coroutines Guide
- Hilt Documentation
Summary
Key Takeaways
- Kotlin-first development - Modern Kotlin patterns, coroutines
- Jetpack Compose - Declarative UI with Material Design 3
- Clean Architecture - Separation of concerns with clear dependency flow
- Hilt dependency injection - Compile-time safe DI with Android optimization
- MVVM pattern - ViewModels survive configuration changes
- Offline-first - Room database for local storage
- Security - EncryptedSharedPreferences, Keystore, biometrics
- Performance - Avoid ANRs, efficient coroutines, lazy loading
- Testing - Unit tests, instrumented tests, Compose UI tests
- Code review standards - Security, memory management, thread safety
Next Steps:
- Explore Android UI Development for Jetpack Compose patterns
- Review Android Security for banking-specific security requirements
- Study Android Architecture for advanced architectural patterns