発車予定表示機能のデータベースを作成します。
エンティティ(テーブル)定義クラスを作成します。
設定データ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 = "" )
Roomのデータベース定義では
@Database( entities = [Settings::class], version = 1, exportSchema = false )
データベース定義は: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" }
:
build-logicのbuild.gradle.ktsに依存ライブラリを追記します。
:
dependencies {
:
implementation(libs.ksp.api)
:
build-logicにkspディレクトリ(パッケージ)を追加し、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()
}
}
エンティティコレクターをKSPの環境へ提供するプロバイダーを作成します。
build-logicのkspディレクトリ(パッケージ)内に、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
)
}
}
プロバイダーをKSPのサービスに登録してインスタンス化します。
build-logicにresources/META-INF/servicesディレクトリを追加し、直下にcom.google.devtools.ksp.processing.SymbolProcessorProviderを作成します。
jp.co.progress_llc.portal.buildlogic.ksp.RoomEntityCollectorProvider