Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Roomの監視可能なクエリのカスタマイズとレガシーコードへの適用
Search
shiita0903
March 11, 2025
Technology
360
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Roomの監視可能なクエリのカスタマイズとレガシーコードへの適用
Qiita Bashで発表したスライドです。
https://increments.connpass.com/event/344818/
shiita0903
March 11, 2025
More Decks by shiita0903
See All by shiita0903
ネストしたdata classの面倒な更新にさようなら!Lensを作って理解するArrowのOpticsの世界
shiita0903
2
1.5k
Other Decks in Technology
See All in Technology
Let's Change Our World—Again
snoozer05
PRO
5
330
スプシの手作業からの解脱 ~更新はClaude Codeに、見せ方と使い方はGASアプリに任せる~
kentarofujii
0
180
フィンテック養成勉強会#68
finengine
0
200
価値について
gree_tech
PRO
0
140
Codex を使った障害対応机上演習の実際
orgachem
PRO
0
260
Metadata - Section 2: Simple Metadata & Meta Tags
mohab1
0
110
ソフトウェア ファクトリー - 再現性のあるソフトウェアの工場づくり / Software factory
iktakahiro
7
5.3k
国際カードブランドルール変更に伴うクレジットカード決済取引仕様変更について
k1style
0
140
ガチで放置できるClaude Code実行環境の作り方.pdf
lamaglama39
0
4.7k
AIネイティブ企業への変革。パーソルキャリアが挑む「データメッシュ×ガバナンス」のアーキテクチャ戦略
techtekt
PRO
0
120
20261017福岡devIO 「AIは"良いダッシュボード"を作れるか?
jtakeda
0
230
AIをリアル世界に解き放つ 〜M5Stack×AIであなたのアイディアを手の平に〜
hamadakoji
1
290
Featured
See All Featured
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
280
Everyday Curiosity
cassininazir
0
360
The SEO Collaboration Effect
kristinabergwall1
1
600
Building Applications with DynamoDB
mza
96
7.2k
The Curse of the Amulet
leimatthew05
3
16k
For a Future-Friendly Web
brad_frost
184
10k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
890
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
460
GraphQLとの向き合い方2022年版
quramy
50
15k
Mind Mapping
helmedeiros
1
400
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
How to Talk to Developers About Accessibility
jct
2
590
Transcript
Roomの監視可能なクエリのカスタマイ ズとレガシーコードへの適用
Room とは SQLiteを扱うためのAndroidXのライブラリ。 特徴 クエリのコンパイル時の検証 コード生成によるボイラープレートの削減 1: https://developer.android.com/training/data-storage/room 2
Roomとは エンティティ(テーブル)の定義 @Entity(tableName = "user") data class User( @PrimaryKey val
id: Int, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "is_premium") val isPremium: Boolean, @ColumnInfo(name = "extra_data") val extraData: String ) 3
Roomとは DAO(Data Access Object)の定義 @Dao interface UserDao { @Insert suspend
fun insert(user: User) @Query("SELECT name FROM user WHERE is_premium") suspend fun selectPremiumUserNames(): List<String> } 4
Roomとは DBの定義 @Database(entities = [User::class], version = 1) abstract class
UserDatabase : RoomDatabase() { abstract fun getUserDao(): UserDao } 5
値の変更をFlowで監視 戻り値の型を Flow でラップするだけでOK @Dao interface UserDao { @Query("SELECT name
FROM user WHERE is_premium") fun selectPremiumUserNamesFlow(): Flow<List<String>> } 6
値の変更をFlowで監視 @Test fun flowSample() = runTest { dao.selectPremiumUserNamesFlow().test { assertEquals(emptyList(),
awaitItem()) dao.insert(User(id = 1, name = "name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 3, name = "name3", isPremium = true, extraData = "")) assertEquals(listOf("name1", "name3"), awaitItem()) } } https://github.com/cashapp/turbine 7
Flowを返す際の注意事項 テーブルの行が更新されるたびにクエリが実行され、値が変わったか どうかにかかわらず Flow に流れる。 dao.insert(User(id = 1, name =
"name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) assertEquals(listOf("name1"), awaitItem()) 2: https://developer.android.com/training/data-storage/room/async-queries#observable 8
Flowを返す際の注意事項 distinctUntilChanged() で、同じ値が連続で流れることを防止。 @Test fun flowDistinctSample() = runTest { dao.selectPremiumUserNamesFlow().distinctUntilChanged().test
{ assertEquals(emptyList(), awaitItem()) dao.insert(User(id = 1, name = "name1", isPremium = true, extraData = "")) assertEquals(listOf("name1"), awaitItem()) dao.insert(User(id = 2, name = "name2", isPremium = false, extraData = "")) // No data is emitted. dao.insert(User(id = 3, name = "name3", isPremium = true, extraData = "")) assertEquals(listOf("name1", "name3"), awaitItem()) } } 9
レガシーコードで起こった問題
レガシーコードで起こった問題1 distinctUntilChanged() では無意味なクエリの実行自体を防ぐことは できない。 短時間での頻繁な更新と時間のかかるクエリの組み合わせが、DBを性 能を低下させる可能性がある。 private fun getExtraDataList(): List<Pair<Int,
String>> = List(size = 1000) { it to "extraData$it" } private suspend fun issue() { getExtraDataList().forEach { (userId, extraData) -> dao.updateExtraData(userId, extraData) } } 11
レガシーコードで起こった問題2 @Transaction を利用したクエリは Flow を返すことができない。 @Transaction suspend fun queries(): List<String>
{ // Flow can't be used for the return value. doSomething() return selectPremiumUserNames() } 12
RoomがDBを監視する仕組み InvalidationTracker がテーブルの更新を検知して通知 SQLiteのTriggerを利用して実装されている publicなので、アプリからも利用可能 13
実装 private fun RoomDatabase.createInvalidationEventFlowWithThrottle( tableName: String, throttleTimeout: Duration = 1.seconds
): Flow<Unit> = callbackFlow { val observer = object : InvalidationTracker.Observer(tableName) { override fun onInvalidated(tables: Set<String>) { trySendBlocking(Unit) } } invalidationTracker.addObserver(observer) send(Unit) // Initial signal to perform first query. awaitClose { invalidationTracker.removeObserver(observer) } } .flowOn(Dispatchers.IO) 14
実装 RxJavaの throttleLatest に対応する関数を作成。 3: https://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Observable.html#throttleLatest- long-java.util.concurrent.TimeUnit-io.reactivex.rxjava3.core.Scheduler-boolean- 15
実装 private fun RoomDatabase.createInvalidationEventFlowWithThrottle( ... ): Flow<Unit> = callbackFlow {
... } .flowOn(Dispatchers.IO) .throttleLatest(throttleTimeout.inWholeMilliseconds) private fun <T> Flow<T>.throttleLatest(timeoutMillis: Long): Flow<T> = this .conflate() .transform { emit(it) delay(timeoutMillis) } 16
実装 @Dao abstract class UserDao(private val database: UserDatabase) { @Transaction
open suspend fun queries(): List<String> { doSomething() return selectPremiumUserNames() } fun selectPremiumUserNamesFlow(): Flow<List<String>> = database .createInvalidationEventFlowWithThrottle(tableName = "user") .map { queries() } .distinctUntilChanged() } 17
まとめ Roomで自動生成される Flow を返す関数の問題点 テーブルが更新されるたびに毎回クエリが実行される @Transaction で実装したクエリでは利用できない InvalidationTracker の利用で、監視可能な関数を自作できる 18