ユーザ用ツール

サイト用ツール


サイドバー

プログレス合同会社

広告

android:studio:application:core-database-module:ksp-build

02.KSPプロセッサープラグインの作成







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

@Database(
  entities = [Settings::class],
  version = 1,
  exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
  abstract fun SettingsDao(): SettingsDao
   :
}

のように、entitiesでエンティティ、AppDatabase内でDAO列挙します。

エンティティやDAOの追加、削除等による列挙ミスを防止するため、列挙するエンティティやDAOを自動生成するKSPプロセッサープラグインを作成します。

KSPライブラリの導入

エンティティやDAOの自動生成に必要なKSPライブラリをバージョンカタログに追記します。

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

5行目
コメント部分のURLを参照して、gradleKotlinに合った最新安定バージョンを指定します。
6行目
コメント部分のURLを参照して、最新安定バージョンを指定します。
10行目~12行目
エンティティやDAOの自動生成に必要なライブラリを追記します。
14行目
Roomの型参照用にライブラリを追記します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

KSPプロセッサーモジュールの作成

KSPプロセッサーモジュール:core:database:kspAndroid Studioモジュール作成ウィザードを使わずにモジュールを作成します。

/core/database直下にkspディレクトリを追加し、その直下にkotlinスクリプトbuild.gradle.ktsを作成してモジュール化します。

plugins {
  alias(libs.plugins.kotlin.jvm)
}

java {
  toolchain {
    languageVersion.set(JavaLanguageVersion.of(libs.versions.java.get().toInt()))
  }
}

dependencies {
  implementation(libs.gradle.ksp)
  implementation(libs.kotlin.poet)
  implementation(libs.kotlin.poet.ksp)
  implementation(libs.room.common)
}

5行目~6行目
ビルドモジュールへの依存をMavenライブラリ形式で行うための定義をしています。
9行目~11行目
エンティティコレクターに必要な依存ライブラリを指定しています。

モジュール作成ウィザードを使わずにモジュールを作成したため、ビルド対象モジュールに手動で追記します。

   :
include(":core:database")
include(":core:database:ksp")
   :

3行目
ビルド対象モジュールに追記します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

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

:core:database:kspモジュールにsrc/main/kotlinディレクトリを作成し、jp.co.progress_llc.portal.core.database.ksp.modelパッケージ(ディレクトリ)を追加します。

作成したパッケージ内にエンティティコレクター用のデータモデルEntityModel.ktを作成します。

package jp.co.progress_llc.portal.core.database.ksp.model

data class EntityModel(
  val packageName: String,
  val simpleName:  String
) {
  val qualifiedName: String = "$packageName.$simpleName"
}

4行目
エンティティのパッケージ名です。
5行目
エンティティのクラス名です。
7行目
エンティティの完全修飾名です。

modelパッケージと同階層にcollectorパッケージ(ディレクトリ)を追加します。

作成したパッケージ内にエンティティコレクターEntityCollector.ktを作成します。

package jp.co.progress_llc.portal.core.database.ksp.collector

import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSClassDeclaration
import jp.co.progress_llc.portal.core.database.ksp.model.EntityModel

class EntityCollector(
  private val resolver: Resolver,
  private val logger:   KSPLogger
) {
  fun collect(): List<EntityModel> {
    return resolver
      .getSymbolsWithAnnotation("androidx.room.Entity")
      .filterIsInstance<KSClassDeclaration>()
      .map { entity ->
        EntityModel(
          packageName = entity.packageName.asString(),
          simpleName  = entity.simpleName.asString()
        )
      }
      .toList()
  }
}

14行目
Roomのエンティティを取得します。
15行目
データクラスに絞り込みます。
16行目~21行目
エンティティモデルのマップを生成します。
22行目
マップをリストに変換します。

DAOコレクターの作成

modelパッケージ内にDAOコレクター用のデータモデルDaoModel.ktを作成します。

package jp.co.progress_llc.portal.core.database.ksp.model

data class DaoModel(
  val packageName: String,
  val simpleName:  String
) {
  val qualifiedName: String = "$packageName.$simpleName"
  val functionName:  String = simpleName.replaceFirstChar { it.lowercaseChar() }
}

4行目
DAOのパッケージ名です。
5行目
DAOのクラス名です。
7行目
DAOの完全修飾名です。
8行目
DAOの関数名(SettingsDaosettingsDao)です。

collectorパッケージ内ににDAOコレクターDaoCollector.ktを作成します。

package jp.co.progress_llc.portal.core.database.ksp.collector

import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSClassDeclaration
import jp.co.progress_llc.portal.core.database.ksp.model.DaoModel

class DaoCollector (
  private val resolver: Resolver,
  private val logger:   KSPLogger
) {
  fun collect(): List<DaoModel> {
    return resolver
      .getSymbolsWithAnnotation("androidx.room.Dao")
      .filterIsInstance<KSClassDeclaration>()
      .map { dao ->
        DaoModel(
          packageName = dao.packageName.asString(),
          simpleName  = dao.simpleName.asString()
        )
      }
      .toList()
  }
}

14行目
RoomDaoを取得します。
15行目
インターフェースクラスに絞り込みます。
16行目~21行目
DAOモデルのマップを生成します。
22行目
マップをリストに変換します。

AppDatabaseGeneratorの作成

collectorパッケージと同階層にgeneratorパッケージ(ディレクトリ)を追加します。

追加したパッケージ内にRoomデータベースを生成するkotlinクラスファイルAppDatabase.ktを自動生成するAppDatabaseGenerator.ktを作成します。

プロバイダーの作成

エンティティコレクターを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(environment)
  }
}

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

インスタンス化

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

:build-logic:kspモジュールの/src/mainセットにresources/META-INF/servicesディレクトリを追加し、直下にcom.google.devtools.ksp.processing.SymbolProcessorProviderを作成します。

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

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

プラグインの作成

エンティティコレクターをプラグインとして各エンティティ定義モジュールから呼び出せるようにします。

:build-logicモジュールのbuild.gradle.ktsにプラグインの作成に必要な依存ライブラリを追記します。

   :
dependencies {
   :
  implementation(libs.gradle.ksp.plugin)
   :

4行目
KSPgradleプラグインを依存ライブラリに追記します。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

:build-logicモジュールのpluginsパッケージ(ディレクトリ)内にRoomEntityCollectorPlugin.ktを作成します。

package jp.co.progress_llc.portal.buildlogic.plugins

import org.gradle.api.Plugin
import org.gradle.api.Project
import com.google.devtools.ksp.gradle.KspExtension

/**
 * RoomのEntityを自動収集するためのGradleプラグイン
 */
class RoomEntityCollectorPlugin : Plugin<Project> {
  override fun apply(project: Project) {
    project.plugins.apply("com.google.devtools.ksp")
    project.dependencies.add(
      "ksp",
      "jp.co.progress_llc.portal.buildlogic:ksp:1.0.0"
    )
    val kspExt = project.extensions.getByType(KspExtension::class.java)
    kspExt.arg("ksp.module.name", project.path)
  }
}

12行目
プラグインを適用するとKSPプラグインが自動的に登録されます。
13行目~16行目
プラグインを適用するとエンティティコレクターが依存ライブラリに登録されます。
17行目~18行目
エンティティコレクターにプラグインを適用したモジュール名(例 :feature:depart:data)を渡します。

プラグインの登録

:build-logicモジュールのbuild.gradle.ktsにエンティティコレクタープラグインを追記します。

   :
gradlePlugin {
   :
  plugins {
    register("RoomEntityCollectorPlugin") {
      id = "build.logic.room.entity.collector"
      implementationClass = "jp.co.progress_llc.portal.buildlogic.plugins.RoomEntityCollectorPlugin"
    }
  }
   :

6行目
プラグインを呼び出すidを定義します。
7行目
プラグインのクラス名をパッケージ名とともに定義します。

プラグインの適用

エンティティを定義している:feature:depart:dataモジュールにエンティティコレクタープラグインを適用します。

plugins {
   :
  id("build.logic.room.entity.collector")
   :

3行目
エンティティコレクタープラグインのidを追記します。

:feature:depart:dataモジュールをビルドして、build/generated/ksp/release/kotlin/generated/room/entities/FeatureDepartDataRoomEntities.ktにエンティティクラスの配列が作成されていることを確認します。

android/studio/application/core-database-module/ksp-build.txt · 最終更新: by プログレス合同会社