ユーザ用ツール

サイト用ツール


サイドバー

プログレス合同会社

広告

android:studio:application:table-initialize-sync

91.テーブルの初期化(同期処理)

今回のアプリケーションでは、必要なかったのですが、データベースの初期化に続けてテーブルを初期化する処理を作成します。

テーブルの初期化は、初期データを記述したJSONファイルを準備して、JSONのキーをRoomのエンティティにマッピングすることにより行います。
これにより、テーブルごとに初期化処理を書く必要がなくなります

マッピングには、Googleが提供する**Gson**ライブラリを使用します。

Gsonの導入

バージョンカタログファイルにGsonの定義を追記します。

[versions]
   :
gson = "2.13.2"                     # https://mvnrepository.com/artifact/com.google.code.gson/gson

[libraries]
   :
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
   :

3行目[versions]
コメント部分のURLを参照して、最新安定バージョンを指定します。
7行目[libraries]
Gsonのライブラリモジュールとバージョンを関連付けします。

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

:core:dataモジュールのbuild.gradle.ktsGsonライブラリの依存を追記します。

   :
dependencies {
   :
  implementation(libs.gson)
   :
}

4行目[dependencies]
Gsonのライブラリを依存ライブラリとして追記します。

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

DAOへの追記

テーブルの初期化処理では、一括INSERTを行いますので、DAOへ追記します。

下記は、RouteDaoの例です。
※初期化処理では、一括INSERTのメソッド名を検索しますので、初期化するすべてのテーブルのDAOでメソッド名を合わせておきます。

   :
  @Insert(onConflict = OnConflictStrategy.REPLACE)
  fun insertAll(routes: List<Route>): List<Long>
   :

2行目~3行目
一括INSERTを追記します。
主キーが重複しているレコードが既に存在している場合は置き換えます。
初期化処理のCoroutine内で使う専用のメソッドですので、suspendを付けません

データベースの初期化処理を同期処理で実行

テーブルの初期化処理が終了する前に、画面の操作がされないように同期処理で初期化処理を実行します。

:appモジュールのPortalApplication.ktを変更します。

   :
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
   :
class HiltApplication: Application() {
   :
  private fun initializeDatabase() {
    runBlocking {
      withContext(Dispatchers.IO) {
        databaseInitializer.initialize()
      }
    }
  }
}

2行目~4行目
同期処理のライブラリをimportします。
9行目
データベースの初期化処理を同期実行にします。
10行目
メインスレッドで重い処理を実行するとアプリ名」は応答していませんとなるので、別スレッド(I/Oスレッド)で実行させます。

データベース初期化処理への追記

データベース初期化処理にテーブルの初期化処理を追記します。

   :
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
   :
class DatabaseInitializer @Inject constructor(
  private val database: AppDatabase
  private val database: AppDatabase,
  @param:ApplicationContext
  private val context: Context
) {
  private var isInitialized = false
  private val basePackage = "${context.packageName}.core.data"
  private val assetsDir   = "tableInitialData"
   :
  fun initialize(): Boolean {
    return if (isInitialized()) {
      // データベースは既に開いています
      true
    } else {
      // データベースを開きます(存在しない場合は作成されます)
      database.openHelper.writableDatabase.isOpen
      // テーブルを初期化します
      initializeTables()
      isInitialized = true
      true
    }
  }

  /**
   * assets/tableInitialDataディレクトリ内のJSONファイルでテーブルを初期化
   */
  private suspend fun initializeTables() {
    // assetsディレクトリ内のJSON初期化ファイルを取得
    val fileNames = context.assets.list(assetsDir) ?: emptyArray()
    for (fileName in fileNames) {
      if (!fileName.endsWith(".json")) continue
      val entityName = fileName.substringBeforeLast('.')
      loadAndInsert(context, database, entityName, "${assetsDir}/${fileName}")
    }
  }

  /**
   * JSON初期化ファイルの内容でテーブルにレコードを全件INSERT
   */
  private suspend fun loadAndInsert(context: Context, database: RoomDatabase, entityName: String, assetPath: String) {
    val entityClass = Class.forName("${basePackage}.${entityName}")
    val daoClass    = Class.forName("${basePackage}.${entityName}Dao")
    // JSON初期化ファイルから初期化データを取得します
    val json = context.assets.open(assetPath).bufferedReader().use { it.readText() }
    val listType = TypeToken.getParameterized(List::class.java, entityClass).type
    val entities = Gson().fromJson<List<*>>(json, listType)
    // INSERTするテーブルのDAOを取得します
    val daoGetter = "${entityName}Dao"
    val daoInstance = database.javaClass.getMethod(daoGetter).invoke(database)
    // insertAllを呼び出します
    val insertMethod = daoInstance::class.java.getMethod("insertAll", List::class.java)
    insertMethod.invoke(daoInstance, entities)
  }
}

2行目~5行目
必要なimportを追記します。
9行目~11行目
DI(Hilt)の注入にContextを追記します。
初期化用データが格納されているassetsディレクトリの場所を取得するため必要になります。
14行目
エンティティやDAOを定義しているパッケージを設定しています。
15行目
assetsディレクトリ内のJSON初期化ファイルが格納されているディレクトリを設定しています。
24行目~25行目
テーブル初期化処理の呼び出しを追記します。
31行目~42行目
assetsディレクトリ内のJSON初期化ファイルを取得して、テーブル単位に初期化処理を呼び出します。
JSON初期化ファイル名とエンティティ名が同じであることを前提としています。
44行目~60行目
テーブル単位に初期化処理を行います。
DAO名はエンティティ名にDaoを付けることを前提としています。
一括INSERTのメソッド名はInseetAllであり、suspendメソッドではないことを前提としています。

初期化データの準備

初期化データは、/core/data/src/main/assets/tableInitialDataディレクトリ内にJSON形式で用意します。

ファイル名は、エンティティ名.jsonにします。

エミュレーターを起動して、アプリケーションを実行します。
※データベースは実行前に削除しておきます。
Device ManagerWipe Dataでエミュレーターを初期化できます。

App Inspectionでレコードが作成されていることを確認します。

android/studio/application/table-initialize-sync.txt · 最終更新: by プログレス合同会社