スキル一覧に戻る

.claude

pgpetrov2001 / SociaApp

0🍴 0📅 2025年12月29日

SKILL.md

# Android Kotlin Development Skill

## Overview
This skill provides comprehensive guidance for developing Android mobile applications using Kotlin and Java, with modern architecture patterns, best practices, and tooling.

## Core Principles

### 1. Language & Style
- **Primary Language**: Kotlin (prefer Kotlin over Java for new code)
- **Java Support**: Maintain Java interoperability when needed
- **Null Safety**: Leverage Kotlin's null safety features extensively
- **Coroutines**: Use Kotlin Coroutines for asynchronous operations (avoid RxJava for new code)
- **Extension Functions**: Use Kotlin extensions to enhance readability

### 2. Architecture Pattern
**MVVM (Model-View-ViewModel)** is the recommended architecture:

```
app/
├── data/
│   ├── local/      # Room database, SharedPreferences
│   ├── remote/     # Retrofit API services
│   ├── model/      # Data classes, entities
│   └── repository/ # Repository pattern implementations
├── domain/
│   ├── usecase/    # Business logic use cases
│   └── model/      # Domain models
├── ui/
│   ├── screens/    # Activities/Fragments organized by feature
│   │   ├── home/
│   │   ├── profile/
│   │   └── settings/
│   ├── components/ # Reusable UI components
│   ├── adapters/   # RecyclerView adapters
│   └── viewmodel/  # ViewModels
└── util/           # Utility classes, extensions
```

**Alternative Architectures**:
- **MVI** (Model-View-Intent): For complex state management
- **Clean Architecture**: For large-scale applications with clear separation of concerns

### 3. Project Structure Best Practices

#### Package Organization
```kotlin
com.yourcompany.appname/
├── data/
├── domain/
├── ui/
├── di/              # Dependency injection (Hilt/Dagger)
├── util/
└── App.kt           # Application class
```

#### Feature-Based Structure (Alternative)
```kotlin
com.yourcompany.appname/
├── feature/
│   ├── auth/
│   │   ├── data/
│   │   ├── domain/
│   │   └── ui/
│   ├── home/
│   └── profile/
├── core/            # Shared resources
└── App.kt
```

## Technology Stack

### Essential Libraries

#### Dependency Injection
```gradle
// Hilt (Recommended)
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"

// Or Koin (Lightweight alternative)
implementation "io.insert-koin:koin-android:3.5.0"
```

#### Networking
```gradle
// Retrofit
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"

// Kotlin Serialization (Alternative to Gson)
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0"
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
```

#### Database
```gradle
// Room
implementation "androidx.room:room-runtime:2.6.0"
implementation "androidx.room:room-ktx:2.6.0"
kapt "androidx.room:room-compiler:2.6.0"
```

#### Asynchronous Operations
```gradle
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"

// Lifecycle-aware coroutines
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
```

#### UI Components
```gradle
// Jetpack Compose (Modern UI)
implementation platform("androidx.compose:compose-bom:2023.10.01")
implementation "androidx.compose.ui:ui"
implementation "androidx.compose.material3:material3"
implementation "androidx.compose.ui:ui-tooling-preview"
implementation "androidx.activity:activity-compose:1.8.1"

// Traditional Views
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "com.google.android.material:material:1.10.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
```

#### Image Loading
```gradle
// Coil (Kotlin-first, recommended for Compose)
implementation "io.coil-kt:coil-compose:2.5.0"

// Glide (Traditional views)
implementation "com.github.bumptech.glide:glide:4.16.0"
kapt "com.github.bumptech.glide:compiler:4.16.0"
```

#### Navigation
```gradle
// Navigation Component
implementation "androidx.navigation:navigation-fragment-ktx:2.7.5"
implementation "androidx.navigation:navigation-ui-ktx:2.7.5"

// Compose Navigation
implementation "androidx.navigation:navigation-compose:2.7.5"
```

## Coding Standards

### 1. Naming Conventions

```kotlin
// Classes: PascalCase
class UserRepository { }
data class UserProfile { }

// Functions: camelCase
fun fetchUserData() { }
fun isValidEmail(): Boolean { }

// Properties: camelCase
val userName: String
private val isLoggedIn: Boolean

// Constants: SCREAMING_SNAKE_CASE
const val MAX_RETRY_COUNT = 3
const val API_BASE_URL = "https://api.example.com"

// Layout files: snake_case
// activity_main.xml, fragment_profile.xml, item_user_list.xml

// Resource IDs: snake_case with type prefix
// btn_submit, tv_title, rv_users, img_profile
```

### 2. File Organization

```kotlin
// Order within a class:
// 1. Companion object
// 2. Properties
// 3. Init blocks
// 4. Constructors
// 5. Override functions
// 6. Public functions
// 7. Private functions
// 8. Inner classes

class ExampleActivity : AppCompatActivity() {
    
    companion object {
        private const val TAG = "ExampleActivity"
        const val EXTRA_USER_ID = "user_id"
    }
    
    private lateinit var binding: ActivityExampleBinding
    private val viewModel: ExampleViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityExampleBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        setupUI()
        observeData()
    }
    
    private fun setupUI() { }
    private fun observeData() { }
}
```

### 3. Kotlin Best Practices

#### Use Data Classes
```kotlin
// Good
data class User(
    val id: String,
    val name: String,
    val email: String
)

// Avoid for mutable entities
data class MutableUser(
    var id: String,  // Prefer immutable
    var name: String
)
```

#### Leverage Sealed Classes for State
```kotlin
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String) : UiState<Nothing>()
}
```

#### Use Extension Functions
```kotlin
// String extensions
fun String.isValidEmail(): Boolean {
    return android.util.Patterns.EMAIL_ADDRESS.matcher(this).matches()
}

// View extensions
fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

// Context extensions
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}
```

#### Scope Functions
```kotlin
// apply: configure object
val user = User().apply {
    name = "John"
    email = "john@example.com"
}

// let: null safety and transformations
val length = user?.name?.let { name ->
    name.length
}

// run: execute block and return result
val result = viewModel.run {
    fetchData()
    processData()
}

// also: additional operations
val savedUser = user.also {
    database.save(it)
    analytics.track("user_saved")
}

// with: multiple operations on object
with(binding.textView) {
    text = "Hello"
    textSize = 16f
    setTextColor(Color.BLACK)
}
```

### 4. ViewModel Pattern

```kotlin
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {
    
    private val _uiState = MutableStateFlow<UiState<User>>(UiState.Loading)
    val uiState: StateFlow<UiState<User>> = _uiState.asStateFlow()
    
    private val _events = MutableSharedFlow<UserEvent>()
    val events: SharedFlow<UserEvent> = _events.asSharedFlow()
    
    init {
        loadUser()
    }
    
    fun loadUser() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val user = userRepository.getUser()
                _uiState.value = UiState.Success(user)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
    
    fun updateUser(name: String) {
        viewModelScope.launch {
            try {
                userRepository.updateUser(name)
                _events.emit(UserEvent.UserUpdated)
            } catch (e: Exception) {
                _events.emit(UserEvent.Error(e.message ?: "Update failed"))
            }
        }
    }
}

sealed class UserEvent {
    object UserUpdated : UserEvent()
    data class Error(val message: String) : UserEvent()
}
```

### 5. Repository Pattern

```kotlin
class UserRepository @Inject constructor(
    private val apiService: ApiService,
    private val userDao: UserDao,
    private val dispatchers: CoroutineDispatchers
) {
    
    suspend fun getUser(userId: String): User = withContext(dispatchers.io) {
        // Try local cache first
        userDao.getUser(userId)?.let { return@withContext it }
        
        // Fetch from network
        val response = apiService.getUser(userId)
        val user = response.toUser()
        
        // Cache locally
        userDao.insert(user)
        
        return@withContext user
    }
    
    fun observeUser(userId: String): Flow<User> {
        return userDao.observeUser(userId)
    }
}
```

### 6. Error Handling

```kotlin
// Use sealed classes for results
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}

// Repository with proper error handling
suspend fun fetchData(): Result<Data> = withContext(Dispatchers.IO) {
    try {
        val response = apiService.getData()
        Result.Success(response)
    } catch (e: IOException) {
        Result.Error(NetworkException("Network error", e))
    } catch (e: HttpException) {
        Result.Error(ServerException("Server error: ${e.code()}", e))
    } catch (e: Exception) {
        Result.Error(UnknownException("Unknown error", e))
    }
}
```

## UI Development

### 1. View Binding (Traditional Views)

```kotlin
class MainActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityMainBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.btnSubmit.setOnClickListener {
            // Handle click
        }
    }
}
```

### 2. Jetpack Compose (Modern Approach)

```kotlin
@Composable
fun UserProfileScreen(
    viewModel: UserViewModel = hiltViewModel(),
    onNavigateBack: () -> Unit
) {
    val uiState by viewModel.uiState.collectAsState()
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("User Profile") },
                navigationIcon = {
                    IconButton(onClick = onNavigateBack) {
                        Icon(Icons.Default.ArrowBack, "Back")
                    }
                }
            )
        }
    ) { padding ->
        when (val state = uiState) {
            is UiState.Loading -> LoadingIndicator()
            is UiState.Success -> UserContent(
                user = state.data,
                modifier = Modifier.padding(padding)
            )
            is UiState.Error -> ErrorMessage(
                message = state.message,
                onRetry = { viewModel.loadUser() }
            )
        }
    }
}
```

### 3. RecyclerView Best Practices

```kotlin
class UserAdapter : ListAdapter<User, UserAdapter.ViewHolder>(UserDiffCallback()) {
    
    var onItemClick: ((User) -> Unit)? = null
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemUserBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return ViewHolder(binding)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }
    
    inner class ViewHolder(
        private val binding: ItemUserBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        
        init {
            binding.root.setOnClickListener {
                onItemClick?.invoke(getItem(bindingAdapterPosition))
            }
        }
        
        fun bind(user: User) {
            binding.tvName.text = user.name
            binding.tvEmail.text = user.email
            // Load image with Coil/Glide
        }
    }
}

class UserDiffCallback : DiffUtil.ItemCallback<User>() {
    override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem.id == newItem.id
    }
    
    override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
        return oldItem == newItem
    }
}
```

## Testing

### 1. Unit Tests

```kotlin
@Test
fun `when user is fetched then state updates to success`() = runTest {
    // Given
    val expectedUser = User("1", "John", "john@example.com")
    coEvery { userRepository.getUser() } returns expectedUser
    
    // When
    viewModel.loadUser()
    
    // Then
    val state = viewModel.uiState.value
    assertTrue(state is UiState.Success)
    assertEquals(expectedUser, (state as UiState.Success).data)
}
```

### 2. Required Testing Libraries

```gradle
testImplementation "junit:junit:4.13.2"
testImplementation "org.mockito.kotlin:mockito-kotlin:5.1.0"
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 "androidx.compose.ui:ui-test-junit4"
```

## Performance Optimization

### 1. Memory Management
- Use `viewModelScope` for coroutines in ViewModels
- Cancel jobs when no longer needed
- Use `Flow` instead of `LiveData` for complex streams
- Avoid memory leaks with proper lifecycle handling

### 2. UI Performance
- Use `RecyclerView` with `DiffUtil` for efficient list updates
- Implement view recycling properly
- Use `ConstraintLayout` to reduce view hierarchy depth
- Lazy load images with Coil/Glide
- Use `ProGuard`/`R8` for code shrinking and obfuscation

### 3. Background Work
```kotlin
// WorkManager for guaranteed background execution
class DataSyncWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {
    
    override suspend fun doWork(): Result {
        return try {
            // Perform sync
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}
```

## Security Best Practices

1. **API Keys**: Store in `local.properties` or use encrypted storage
2. **ProGuard/R8**: Enable code obfuscation
3. **Network Security**: Use HTTPS, implement certificate pinning
4. **Data Encryption**: Use `EncryptedSharedPreferences` and `EncryptedFile`
5. **Input Validation**: Validate all user inputs
6. **Permissions**: Request only necessary permissions, handle runtime permissions properly

```kotlin
// Encrypted SharedPreferences
val sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build(),
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
```

## Build Configuration

### Gradle Best Practices

```kotlin
// build.gradle.kts (app module)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.dagger.hilt.android")
    kotlin("kapt")
}

android {
    namespace = "com.yourcompany.appname"
    compileSdk = 34
    
    defaultConfig {
        applicationId = "com.yourcompany.appname"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0.0"
        
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }
    
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        debug {
            isDebuggable = true
        }
    }
    
    buildFeatures {
        viewBinding = true
        compose = true
    }
    
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.3"
    }
    
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    
    kotlinOptions {
        jvmTarget = "17"
    }
}
```

## Common Patterns

### 1. Singleton with Hilt
```kotlin
@Singleton
class AppPreferences @Inject constructor(
    @ApplicationContext private val context: Context
) {
    private val prefs = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
    
    var isLoggedIn: Boolean
        get() = prefs.getBoolean("is_logged_in", false)
        set(value) = prefs.edit().putBoolean("is_logged_in", value).apply()
}
```

### 2. Network State Monitoring
```kotlin
class NetworkMonitor @Inject constructor(
    @ApplicationContext private val context: Context
) {
    val isConnected: Flow<Boolean> = callbackFlow {
        val connectivityManager = context.getSystemService<ConnectivityManager>()!!
        
        val callback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                trySend(true)
            }
            
            override fun onLost(network: Network) {
                trySend(false)
            }
        }
        
        connectivityManager.registerDefaultNetworkCallback(callback)
        
        awaitClose {
            connectivityManager.unregisterNetworkCallback(callback)
        }
    }.distinctUntilChanged()
}
```

## Documentation Standards

- Add KDoc comments for public APIs
- Document complex logic with inline comments
- Maintain README.md with setup instructions
- Keep CHANGELOG.md for version tracking
- Use TODO comments with owner: `// TODO(username): Description`

## Checklist for New Features

- [ ] Follow MVVM/Clean Architecture
- [ ] Implement proper error handling
- [ ] Add loading states
- [ ] Handle configuration changes
- [ ] Write unit tests
- [ ] Update documentation
- [ ] Check for memory leaks
- [ ] Test on different screen sizes
- [ ] Verify accessibility
- [ ] Add ProGuard rules if needed

## Resources

- [Android Developers](https://developer.android.com/)
- [Kotlin Documentation](https://kotlinlang.org/docs/home.html)
- [Android Architecture Components](https://developer.android.com/topic/libraries/architecture)
- [Material Design Guidelines](https://m3.material.io/)

---

**Version**: 1.0  
**Last Updated**: December 2024  
**Maintained By**: Your Team Name