Android 初心者が Room を使おうとしてハマったこと

※ この記事は 2023/08/14 時点の情報を元に作成しています。

Android アプリで DB 操作を行うために、Room というライブラリを利用しました。
以下の公式を参考に取り組んだのですが、一筋縄ではいかなかったので、遭遇したエラーと解消方法について本記事に記録しておきます。

[Room を使用してローカル データベースにデータを保存する | セットアップ](https://developer.android.com/training/data-storage/room?hl=ja#kotlin)

## 環境

- Android Studio Flamingo | 2022.2.1 Patch 2
- macOS 13.4.1
- compileSdk = targetSdk = 34

## 結論

`build.gradle (Project :...)`に追記する内容は、以下のとおりです。

```gradle
plugins {
    ...
    id 'com.google.devtools.ksp' version '1.9.0-1.0.13' apply false
}
```

`build.gradle (Module :app)`に追記する内容は、以下のとおりです。

```gradle
plugins {
    ...
    id 'kotlin-android'
    id 'com.google.devtools.ksp'
}
...
dependencies {
    ...
    def room_version = "2.5.2"

    implementation "androidx.room:room-runtime:$room_version"
    ksp "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"
    implementation "androidx.room:room-rxjava3:$room_version"
    implementation "androidx.room:room-guava:$room_version"
    testImplementation "androidx.room:room-testing:$room_version"
    implementation "androidx.room:room-paging:$room_version"
}
```

## やったことと遭遇したエラー

Android も Kotolin も初めての状態だったので、何も考えず公式のコードをコピペしました。
その時に遭遇したエラーがこちらです。

```
A problem occurred evaluating project ':app'.
> Could not find method kapt() for arguments [androidx.room:room-compiler:2.5.2] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
```

モジュールが足りなさそうなことはエラーメッセージからわかりましたが、どう対処すればいいかはエラーメッセージに書いてありません。
ググった結果、`build.gradle (Module :app)`に以下を追加しました。

```gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-android' //追加
    id 'kotlin-kapt' //追加
}
```

同様のエラーが`ksp()`に対しても、発生。
ググった結果、`build.gradle (Module :app)`に以下を追加しました。

```gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-android' //追加
    id 'kotlin-kapt' //追加
    id 'com.google.devtools.ksp'  //更に追加
}
```

この時、`build.gradle (Project :...)`にも`com.google.devtools.ksp`を追加しています。

そして発生したエラーがこちら。

```
警告:Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.
```

ググって、一番簡単そうな`exportSchema = false`を指定する方法を採用しました。

```kotlin
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
```

そして発生したエラーがこちら。

```
エラー: クラス ...AppDatabase_Implが重複しています
```

Clean Project しても build ディレクトリ配下を削除しても解消せず。
散々ググった結果、kapt と ksp の両方を指定しているために、それぞれでクラスを自動生成して重複が発生する自体となった模様。

kapt の問題点を解消したのが ksp とのことで、kapt に関する記載を削除しました。

```gradle
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-android' //追加
    // id 'kotlin-kapt' //削除
    id 'com.google.devtools.ksp'  //更に追加
}
...
dependencies {
    ...
    def room_version = "2.5.2"

    implementation "androidx.room:room-runtime:$room_version"
    // annotationProcessor("androidx.room:room-compiler:$room_version") //削除
    // kapt("androidx.room:room-compiler:$room_version") //削除
    ksp "androidx.room:room-compiler:$room_version"
```

調査の過程で、kotlin でアノテーションを使用するために ksp が必要だったことが判明しました。
そのため、アノテーション関連のエラーメッセージの対応として追加していた`exportSchema = false`も削除しました。

結果、公式から変更した箇所は、`build.gradle (Module :app)`の内容だけになりました。

```gradle
plugins {
    ...
    id 'kotlin-android'  //追加
    id 'com.google.devtools.ksp'  //追加
}
...
dependencies {
    ...
    def room_version = "2.5.2"

    implementation "androidx.room:room-runtime:$room_version"
    // annotationProcessor("androidx.room:room-compiler:$room_version") //削除
    // kapt("androidx.room:room-compiler:$room_version") //削除
    ksp "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-rxjava2:$room_version"
    implementation "androidx.room:room-rxjava3:$room_version"
    implementation "androidx.room:room-guava:$room_version"
    testImplementation "androidx.room:room-testing:$room_version"
    implementation "androidx.room:room-paging:$room_version"
}
```

やっぱり何も理解しないでコピペすると、ハマりますね。