Room の使い方
概要
Android Kotlin Fundamentals 06.1 (Room) の備忘録です。ポイントとなるコードのみを抜粋しています。
目次
確認環境
- AndroidStudio 3.5
- compileSdkVersion 28
- minSdkVersion 19
- Gradle 5.4.1
- Kotlin 1.3.11
参考情報
- Android Kotlin Fundamentals Course
- 06.1 の内容から抜粋
解説
build.gradle
buildscript { ext { kotlin_version = '1.3.11' archLifecycleVersion = '1.1.1' room_version = '2.0.0' coroutine_version = '1.0.0' gradleVersion = '3.3.0' navigationVersion = '1.0.0-alpha08' dataBindingCompilerVersion = gradleVersion // Always need to be the same. } repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:$gradleVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" } } ...
app/build.gradle
... dependencies { ... implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" }
SleepNight.kt
... @Entity(tableName = "daily_sleep_quality_table") data class SleepNight( @PrimaryKey(autoGenerate = true) var nightId: Long = 0L, @ColumnInfo(name = "start_time_milli") val startTimeMilli: Long = System.currentTimeMillis(), @ColumnInfo(name = "end_time_milli") var endTimeMilli: Long = startTimeMilli, @ColumnInfo(name = "quality_rating") var sleepQuality: Int = -1 )
SleepDatabaseDao.kt
... @Dao interface SleepDatabaseDao { @Insert fun insert(night: SleepNight) @Update fun update(night: SleepNight) @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key") fun get(key: Long): SleepNight? @Query("DELETE FROM daily_sleep_quality_table") fun clear() @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1") fun getTonight(): SleepNight? @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC") fun getAllNights(): LiveData<List<SleepNight>> }
- アノテーションは Insert, Update, Delete, Query の 4 つ
- Delete アノテーションは 1 レコード削除の場合に使う
- LiveData を戻り値にするとデータベースのデータが変更された場合に変更を通知する
- (変更の検知はデータベース単位?テーブル単位?便利だけどパフォーマンスは問題にならない?)
SleepDatabase.kt
... @Database(entities = [SleepNight::class], version = 1, exportSchema = false) abstract class SleepDatabase : RoomDatabase() { abstract val sleepDatabaseDao: SleepDatabaseDao companion object { @Volatile private var INSTANCE: SleepDatabase? = null fun getInstance(context: Context): SleepDatabase { synchronized(this) { var instance = INSTANCE if (instance == null) { instance = Room.databaseBuilder(context.applicationContext, SleepDatabase::class.java, "sleep_history_database") .fallbackToDestructiveMigration() .build() INSTANCE = instance } return instance } } } }
- version はスキーマが変更された場合に上げる
- スキーマを変更する場合にはマイグレーションが必要 (Understanding migrations with Room - Android Developers - Medium)
- fallbackToDestructiveMigration メソッドを使うことで、マイグレーションなしでデータベースを破棄、再構築できる
- exportSchema はスキーマ定義をファイルに残す場合は true
- 但し、アノテーションプロセッサの引数 (
room.schemaLocation
) にファイルを保存するディレクトリを指定する必要がある - バージョン管理システムでスキーマのバージョン管理をする用途で使い、配布するアプリをビルドする際には false にする
- 但し、アノテーションプロセッサの引数 (
- Volatile はマルチスレッドの場合に最新の値を全スレッドで常に読み込める様にする (Javaの理論と実践: volatile を扱う)
- INSTANCE プロパティはマルチスレッドを考慮して読み書き時に synchronized でロックする
SleepDatabaseTest.kt
... @RunWith(AndroidJUnit4::class) class SleepDatabaseTest { private lateinit var sleepDao: SleepDatabaseDao private lateinit var db: SleepDatabase @Before fun createDb() { val context = InstrumentationRegistry.getInstrumentation().targetContext db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java) .allowMainThreadQueries() .build() sleepDao = db.sleepDatabaseDao } @After @Throws(IOException::class) fun closeDb() { db.close() } @Test @Throws(Exception::class) fun insertAndGetNight() { val night = SleepNight() sleepDao.insert(night) val tonight = sleepDao.getTonight() assertEquals(tonight?.sleepQuality, -1) } }
- inMemoryDatabaseBuilder を使うことでテスト毎にデータベースをクリアする
- allowMainThreadQueries を実行することでデータベースへの問い合わせを MainThread で実行できる様にする
- 通常は MainThread でデータベースに問い合わせるとエラーになる