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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
IoTデバイスを繋げるプロトコルMatterをAndroidで使用する方法
harutiro
0
120
子育てエンジニアの可処分時間が減っても成長を諦めない戦い方
sudoakiy
4
4.2k
ai_cording_with_k8s_knowledge.pdf
mochizuki875
1
320
Essential architectural thinking (revised edition)
ufried
0
110
AIに攻撃される前に、AIに攻撃させる
tsuchikazu
0
160
AIがコードを書く時代に、エンジニアは何で自分の価値を証明するのか / In an era where AI writes code how do engineers prove their value
carta_engineering
0
610
AI駆動開発の時代になったのでトヨタ生産方式から見直す
terurou
5
5.4k
OR学会2026年秋期大会_機械学習と数理最適化の融合による、配合レシピ最適化システム開発事例
brainpadpr
0
110
初対面のメンバーがチームを組み、Claude Codeでゼロからサービスをリリース
mkdev10
1
110
おそらく日本で唯一のDevRelインターン生として
husengs7
0
170
Snapshot Testing in Practice: Predictable and Reliable SwiftUI Views
fespinoza
0
160
Lambda MicroVMsが分からなすぎたので使い所を1から考えてみた
tsukuboshi
2
440
Featured
See All Featured
sira's awesome portfolio website redesign presentation
elsirapls
0
450
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
2
6.3k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
400
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
510
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
350
Leo the Paperboy
mayatellez
10
2.4k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
280
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