目次

01.データベースの作成

発車予定表示機能のデータベースを作成します。

エンティティの作成

エンティティ(テーブル)定義クラスを作成します。

設定データSettings.ktを作成します。

package jp.co.progress_llc.portal.feature.depart.data

import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.ColumnInfo

@Entity(tableName = "settings")
data class Settings(
  @PrimaryKey()
  @ColumnInfo(name = "item_name")
  val itemName:  String = "",
  @ColumnInfo(name = "item_value")
  val itemValue: String = "" 
)

7行目
データベース上のテーブル名をsettingsにしています。
9行目
itemNameを主キーにしています。
10行目、12行目
データベース上のカラム名を定義しています。
11行目、13行目
アプリケーションでのカラム名と属性を定義しています。

エンティティコレクターの作成

Roomのデータベース定義では

@Database(
  entities = [Settings::class],
  version = 1,
  exportSchema = false
)

のように、entitiesでデータベース内のエンティティを列挙します。

データベース定義は:appモジュールで行いますが、そうすると各機能モジュールのエンティティを:appモジュールが知る必要があり、モジュール結合を疎にするという設計方針から外れてしまいます。

そこで、各機能モジュールのエンティティ定義を自動で収集するKSP(Kotlin Symbol Processing)を使ったエンティティコレクターを作成することにします。

KSPを使ってKotlinコードを自動生成するために必要な依存ライブラリ等をバージョンカタログに追記します。

[versions]
   :
# ./gradlew -versionで表示されるKotlinのバージョンに適した最新バージョンにします(自動で上げない)
gradle-ksp = "2.0.21-1.0.28"        # https://mvnrepository.com/artifact/com.google.devtools.ksp/symbol-processing-api
   :
[libraries]
   :
ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "gradle-ksp" }
   :

6行目
コメント部分のURLを参照して、Symbol Processing APIのバージョンを指定します。
gradleで使用しているKotlinのバージョンに適した最新バージョンを指定します。
8行目
KSPのライブラリを指定しています。

build-logicbuild.gradle.ktsに依存ライブラリを追記します。

   :
dependencies {
   :
  implementation(libs.ksp.api)
   :

4行目
KSPSymbol Processing APIライブラリを依存に追記します。

build-logickspディレクトリ(パッケージ)を追加し、RoomのエンティティコレクターRoomEntityCollector.ktを作成します。

package jp.co.progress_llc.portal.buildlogic.ksp

import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import java.io.OutputStreamWriter

/**
 * Roomの@Entityアノテーションが付いたクラスを収集するKSPプロセッサー
 */
class RoomEntityCollector(
  private val codeGenerator: CodeGenerator,
  private val logger: KSPLogger
) : SymbolProcessor {
  override fun process(resolver: Resolver): List<KSAnnotated> {
    // Roomの@Entityアノテーションが付いたクラスを収集します
    val entities = resolver
      .getSymbolsWithAnnotation("androidx.room.Entity")
      .filterIsInstance<KSClassDeclaration>()
      .toList()
    if (entities.isEmpty())     return emptyList()
    // 収集したクラスをファイルへ書き出します
    val file = codeGenerator.createNewFile(
      Dependencies(false),
      "",
      "RoomEntities",
      "kt"
    )
    OutputStreamWriter(file).use { writer ->
      writer.appendLine("// このファイルは自動生成されます。手動で編集しないでください。")
      writer.appendLine("package jp.co.progress_llc.portal.buildlogic.generated")
      writer.appendLine()
      writer.appendLine("/**")
      writer.appendLine(" * 自動収集されたRoom Entityクラスのリスト")
      writer.appendLine(" */")
      writer.appendLine("object RoomEntities {")
      writer.appendLine("  /**")
      writer.appendLine("   * すべてのEntityクラスの配列")
      writer.appendLine("   */")
      writer.appendLine("  val entities: Array<Class<*>> =")
      writer.appendLine(
        entities.joinToString(
          prefix = "arrayOf(",
          postfix = ")"
        ) { "${it.qualifiedName!!.asString()}::class" })
      writer.appendLine("}")
    }
    return emptyList()
  }
}

16行目~20行目
Roomの@Entityアノテーションが付いたクラスを収集しています。
23行目~28行目
収集したクラスを書き出すファイルを指定しています。
29行目~47行目
ファイルにKotlinコードとして書き出しています。

プロバイダーの作成

エンティティコレクターをKSPの環境へ提供するプロバイダーを作成します。

build-logickspディレクトリ(パッケージ)内に、RoomのエンティティコレクターをKSPの環境へ提供するRoomEntityCollectorProvider.ktを作成します。

package jp.co.progress_llc.portal.buildlogic.ksp

import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider

/**
 * RoomEntityCollectorのプロバイダー
 */
class RoomEntityCollectorProvider : SymbolProcessorProvider {
  override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
    return RoomEntityCollector(
      codeGenerator = environment.codeGenerator,
      logger = environment.logger
    )
  }
}

12行目~15行目
エンティティコレクターを呼び出しています。

KSPへの登録

プロバイダーをKSPのサービスに登録してインスタンス化します。

build-logicresources/META-INF/servicesディレクトリを追加し、直下にcom.google.devtools.ksp.processing.SymbolProcessorProviderを作成します。

jp.co.progress_llc.portal.buildlogic.ksp.RoomEntityCollectorProvider

1行目
RoomのエンティティコレクターのプロバイダーをKSPのプロセッサーに登録して、インスタンス化しています。