WHEN: Android Kotlin code review, Jetpack Compose patterns, Coroutines/Flow checks, ViewModel structure analysis WHAT: Compose best practices + Coroutines patterns + State management + Memory leak detection + Performance optimization WHEN NOT: KMP shared code → kotlin-multiplatform-reviewer, Backend → kotlin-spring-reviewer
Install
npx skillscat add physics91/claude-vibe/kotlin-android-reviewer Install via the SkillsCat registry.
The kotlin-android-reviewer skill analyzes Android Kotlin code for Jetpack Compose, Coroutines, Flow, and ViewModel best practices, identifying issues such as improper side‑effects, missing remember usage, state hoisting problems, memory leaks, and performance inefficiencies. It is used during Android code reviews to ensure adherence to Compose and Coroutine patterns and to detect architectural or runtime issues.
Kotlin Android Reviewer Skill
Purpose
Reviews Android Kotlin code for Jetpack Compose, Coroutines, Flow, and ViewModel best practices.
When to Use
- Android Kotlin project code review
- "Compose pattern", "Coroutines", "Flow", "ViewModel" mentions
- Android performance, memory leak inspection
- Projects with Android plugin in
build.gradle.kts
Project Detection
com.android.applicationorcom.android.libraryin build.gradleAndroidManifest.xmlexistssrc/main/javaorsrc/main/kotlindirectories
Workflow
Step 1: Analyze Project
**Kotlin**: 1.9.x
**Compose**: 1.5.x
**minSdk**: 24
**targetSdk**: 34
**Architecture**: MVVM + Clean ArchitectureStep 2: Select Review Areas
AskUserQuestion:
"Which areas to review?"
Options:
- Full Android pattern check (recommended)
- Jetpack Compose UI patterns
- Coroutines/Flow usage
- ViewModel/State management
- Memory leaks/Performance
multiSelect: trueDetection Rules
Jetpack Compose Patterns
| Check | Recommendation | Severity |
|---|---|---|
| Side-effect in Composable | Use LaunchedEffect/SideEffect | HIGH |
| Object creation without remember | Use remember { } | HIGH |
| Missing State hoisting | Hoist state to parent | MEDIUM |
| Missing derivedStateOf | Use for derived state | LOW |
| LazyColumn without key | Add key parameter | HIGH |
// BAD: Object creation without remember
@Composable
fun MyScreen() {
val list = mutableListOf<String>() // New every recomposition
}
// GOOD: Use remember
@Composable
fun MyScreen() {
val list = remember { mutableListOf<String>() }
}
// BAD: Direct suspend call in Composable
@Composable
fun MyScreen(viewModel: MyViewModel) {
viewModel.loadData() // Side-effect!
}
// GOOD: Use LaunchedEffect
@Composable
fun MyScreen(viewModel: MyViewModel) {
LaunchedEffect(Unit) {
viewModel.loadData()
}
}Coroutines Patterns
| Check | Recommendation | Severity |
|---|---|---|
| GlobalScope usage | Use viewModelScope/lifecycleScope | CRITICAL |
| Missing Dispatcher | Specify Dispatchers.IO/Default | MEDIUM |
| Missing exception handling | try-catch or CoroutineExceptionHandler | HIGH |
| runBlocking abuse | Convert to suspend function | HIGH |
// BAD: GlobalScope
GlobalScope.launch {
repository.fetchData()
}
// GOOD: viewModelScope
viewModelScope.launch {
repository.fetchData()
}
// BAD: Network on Main
viewModelScope.launch {
val result = api.getData() // NetworkOnMainThreadException
}
// GOOD: IO Dispatcher
viewModelScope.launch {
val result = withContext(Dispatchers.IO) {
api.getData()
}
}Flow Patterns
| Check | Recommendation | Severity |
|---|---|---|
| Direct collect in Composable | Use collectAsState | HIGH |
| SharedFlow without replay | Set appropriate replay value | MEDIUM |
| Nullable StateFlow initial | Provide meaningful initial value | MEDIUM |
// BAD: Direct collect in Composable
@Composable
fun MyScreen(viewModel: MyViewModel) {
var data by remember { mutableStateOf<Data?>(null) }
LaunchedEffect(Unit) {
viewModel.dataFlow.collect { data = it }
}
}
// GOOD: collectAsState
@Composable
fun MyScreen(viewModel: MyViewModel) {
val data by viewModel.dataFlow.collectAsState()
}
// BAD: Nullable StateFlow initial
private val _state = MutableStateFlow<UiState?>(null)
// GOOD: Sealed class with clear initial state
private val _state = MutableStateFlow<UiState>(UiState.Loading)ViewModel Patterns
| Check | Issue | Severity |
|---|---|---|
| Direct Context reference | Memory leak risk | CRITICAL |
| View reference | Memory leak risk | CRITICAL |
| Missing SavedStateHandle | Process death handling | MEDIUM |
| Bidirectional data flow | Use UiState + Event pattern | MEDIUM |
// BAD: Activity Context reference
class MyViewModel(private val context: Context) : ViewModel()
// GOOD: Application Context with Hilt
class MyViewModel(
@ApplicationContext private val context: Context
) : ViewModel()
// BAD: Bidirectional binding
class MyViewModel : ViewModel() {
var name = MutableLiveData<String>()
}
// GOOD: Unidirectional + sealed class
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun onNameChanged(name: String) {
_uiState.update { it.copy(name = name) }
}
}Memory Leak Detection
| Check | Problem | Solution |
|---|---|---|
| Inner class with outer reference | Activity leak | WeakReference or static |
| Unremoved Listener | Memory leak | Remove in onDestroy |
| Uncancelled Coroutine Job | Job leak | Structured concurrency |
| Unreleased Bitmap | OOM risk | recycle() or Coil/Glide |
Response Template
## Android Kotlin Code Review Results
**Project**: [name]
**Kotlin**: 1.9.x | **Compose**: 1.5.x
**Files Analyzed**: X
### Jetpack Compose
| Status | File | Issue |
|--------|------|-------|
| HIGH | ui/HomeScreen.kt | Object creation without remember (line 45) |
| MEDIUM | ui/ProfileScreen.kt | State hoisting recommended |
### Coroutines/Flow
| Status | File | Issue |
|--------|------|-------|
| CRITICAL | data/Repository.kt | GlobalScope usage (line 23) |
| HIGH | viewmodel/MainViewModel.kt | Missing exception handling |
### ViewModel/State
| Status | File | Issue |
|--------|------|-------|
| HIGH | viewmodel/DetailViewModel.kt | Activity Context reference |
### Recommended Actions
1. [ ] GlobalScope → viewModelScope
2. [ ] Add remember { }
3. [ ] Apply UiState sealed class patternBest Practices
- Compose: Prefer Stateless Composable, State hoisting
- Coroutines: Structured concurrency, appropriate Dispatcher
- Flow: Distinguish Hot/Cold Flow, StateFlow for UI state
- ViewModel: Unidirectional data flow, avoid Context
- Testing: Turbine for Flow, Compose test rules
Integration
code-reviewerskill: General Kotlin code qualitykotlin-multiplatform-reviewerskill: KMP shared codetest-generatorskill: Android test generation
Notes
- Based on Compose 1.0+
- Kotlin 1.9+, Coroutines 1.7+ recommended
- Supports Hilt/Dagger DI patterns