はじめに
AndroidはXML→Compose、AsyncTask→Coroutines、Dagger→HiltとパラダイムシフトしてきたためClaude Codeが旧パターンを生成しやすい。本記事はKotlin/Android Nativeに特化し、CLAUDE.mdで旧来パターンを封じる方法を整理する。
Claude Codeが生成しやすい古いAndroidパターンを正す
ViewBinding(XMLレイアウト参照)→ Jetpack Compose
// ❌ ViewBinding
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button.setOnClickListener { binding.textView.text = "Clicked!" }
// ✅ Jetpack Compose
@Composable
fun MainScreen(text: String, onButtonClick: () -> Unit) {
Column(Modifier.fillMaxSize().padding(16.dp)) {
Text(text = text)
Button(onClick = onButtonClick) { Text("Click me") }
}
}
AsyncTask → viewModelScope + Coroutines
AsyncTaskはAPI Level 30(Android 11)でdeprecated。
// ❌ AsyncTask(API Level 30でdeprecated)
class FetchTask : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg p: Void) = api.fetchData()
override fun onPostExecute(result: String) { textView.text = result }
}
FetchTask().execute()
// ✅ viewModelScope.launch(核心部分)
fun fetchData() {
viewModelScope.launch {
_uiState.value = try { UiState.Success(repo.fetchData()) }
catch (e: Exception) { UiState.Error(e.message ?: "Error") }
}
}
viewModelScopeはViewModelが破棄される際に自動キャンセルする。I/O処理はRepository層のsuspend関数内でwithContext(Dispatchers.IO)を使う。
ViewModelFactory手動実装 + LiveData → @HiltViewModel + StateFlow
// ❌ ViewModelFactory手動実装 + LiveData
class Factory(val r: DataRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(c: Class<T>): T = OldViewModel(r) as T
}
class OldViewModel(val r: DataRepository) : ViewModel() {
val data: LiveData<String> = MutableLiveData()
}
// ✅ @HiltViewModel + StateFlow
@HiltViewModel
class MainViewModel @Inject constructor(val repo: DataRepository) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState
}
// Composable: val s by vm.uiState.collectAsStateWithLifecycle()
// 依存: lifecycle-runtime-compose:2.10.0
CLAUDE.mdで制御する完全テンプレート
# Android/Kotlin プロジェクト設定
技術スタック: Kotlin / Jetpack Compose / Coroutines+Flow / Hilt 2.x / MVVM+Repository
## UI(Jetpack Compose)
- UIはすべて @Composable 関数で実装(XMLレイアウト禁止・setContentView()禁止)
- State HoistingパターンでステートレスComposableを優先する
- リスト: LazyColumn / LazyRow(RecyclerView禁止)
## 状態管理
- ViewModel: MutableStateFlow(private)→ StateFlow(public)で公開(LiveData禁止)
- UiState: sealed class(Loading・Success・Error)
- Composable: collectAsStateWithLifecycle() で収集(lifecycle-runtime-compose:2.10.0)
## 非同期処理
- AsyncTask・Thread・Handler 禁止(AsyncTask はAPI Level 30 deprecated)
- ViewModel: viewModelScope.launch / I/O: withContext(Dispatchers.IO)
## 依存注入(Hilt)
- @HiltAndroidApp / @AndroidEntryPoint / @HiltViewModel を使う
- @Inject constructor で宣言(ViewModelFactory手動実装禁止)
## 禁止事項
- nullable強制解除(!!)禁止 → ?.let {} またはElvis演算子(?:)
- XMLレイアウト・LiveData・ViewModelFactory手動実装は新規コードで使用禁止
PostToolUseフックでktlintを自動実行する
.ktファイル編集後にktlintで自動フォーマットする(事前にbrew install ktlintが必要)。
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "FILE=$(jq -r '.tool_input.file_path // empty'); [[ \"$FILE\" == *.kt ]] && ktlint --format \"$FILE\" 2>/dev/null; true"
}]
}]
}
}
EM視点──AndroidチームへのClaude Code展開
新規プロジェクトはCLAUDE.mdを最初から設置してCompose専用として始める。XMLが混在する既存プロジェクトでは「新規画面はCompose、既存XMLはマイグレーション対象として触らない」と明記して段階移行を管理する。CLAUDE.mdと.claude/settings.jsonをリポジトリにコミットすれば、JuniorエンジニアがClaude Codeを使っても旧パターンが混入しない。
まとめ
Claude CodeがAndroidで生成しやすい旧パターンはViewBinding・AsyncTask・ViewModelFactory+LiveDataだ。本記事のCLAUDE.mdテンプレートを今すぐプロジェクトにコミットしてほしい。ktlintのPostToolUseフックを合わせて設定すれば、スタイル違反もその場で自動修正される。

コメント