Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
DataStoreを導入してみた
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
kobaken
April 20, 2023
Technology
390
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
DataStoreを導入してみた
kobaken
April 20, 2023
More Decks by kobaken
See All by kobaken
複数行のTextで中間省略(…)を実現する
kobaken0029
0
71
Jetpack Compose Preview実践ガイド
kobaken0029
0
150
Serializable / Parcelableとの上手な付き合い方
kobaken0029
0
190
Kotlinの好きなところ
kobaken0029
0
1.4k
Compose駆動開発のためのマルチモジュール化
kobaken0029
0
260
Epoxyを用いたレイアウト構築術
kobaken0029
1
260
Androidエンジニアが1週間でiOSアプリ開発を学び、1ヶ月で大規模アプリ開発にJOINした話
kobaken0029
0
3.9k
Modern REST Communicate for Android
kobaken0029
0
1.6k
AndroidでモダンREST通信してみたった
kobaken0029
0
280
Other Decks in Technology
See All in Technology
データベース研修【MIXI 26新卒技術研修】
mixi_engineers
PRO
1
720
Retriever と Reranker、結局どうする?
kazuaki
1
530
ここは地獄!つらい朝会を体験することで、チームとしてのより良い振る舞いに気づくワークショップ / The stand-up meeting from hell in the game industry
scrummasudar
0
440
ウォーターフォール開発案件のPMとしてAI活用を模索している話
hatahata021
2
230
PLaMo 3.0 Primeの事後学習
pfn
PRO
0
210
AIツールを導入しても生産性はあがらない? カオナビが直面した 3つの壁と乗り越え方。/ Overcoming 3 Barriers to AI-Driven Productivity at kaonavi
kaonavi
0
480
クラウドセキュリティ入門 ~安全なクラウド利用のための基礎知識~
lhazy
0
430
信頼できるテスティングAIをどう育てるか?
odan611
0
170
AI ネイティブな組織に Gemini Enterprise Agent Platform がなぜ必要なのか
asei
0
110
LLMリーダーボードアップデートに向けたAgentic Math_SWEのトレースについて
nejumi
0
160
基調講演:人とAIをつなぐIoTの今と未来 ー 「フィジカル」と「デジタル」が出会うその先へ【SORACOM Discovery 2026】
soracom
PRO
0
360
A Bag-of-Documents Model for Query Specificity
dtunkelang
0
180
Featured
See All Featured
Building an army of robots
kneath
306
46k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
190
What's in a price? How to price your products and services
michaelherold
247
13k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
YesSQL, Process and Tooling at Scale
rocio
174
15k
The Cult of Friendly URLs
andyhume
79
7k
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
270
Designing for Performance
lara
611
70k
Scaling GitHub
holman
464
140k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
360
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Documentation Writing (for coders)
carmenintech
77
5.4k
Transcript
@kobaken DataStoreΛಋೖͯ͠Έͨ
ࣗݾհ • kobakenʢ@koba_dog_ʣ • pixivࣄۀຊ෦ϚϯΨ෦ • AndroidΞϓϦΛͭͬͯ͘·͢ • ࠷ۙϙέΧͱϫϯϐʔεΧʔυ͕͍ʂ
ͱ͜ΖͰΈͳ͞Μ
pixivίϛοΫͬͯ͝ଘͰ͔͢ʁ
ίϛοΫΛઆ໌Ͱ͖Δ ͳΜ͔͍͍ײ͡ͷը૾
ΈͯͶʂ https://inside.pixiv.blog/2023/01/25/120000
ؓٳ
DataStoreͱ
DataStoreͱ • σʔλΛϩʔΧϧʹӬଓԽͰ͖ΔσʔλετϨʔδιϦϡʔγϣϯ • Android Jetpackʹؚ·Ε͍ͯΔϥΠϒϥϦ • 20209݄ʹ1.0.0-alpha1͕ެ։͞Εͨ • ݱࡏ҆ఆ൛ͱͯ͠1.0.0͕ϦϦʔε͞Ε͍ͯΔ
DataStoreͷಛ • Kotlin CoroutinesʹରԠ͍ͯ͠Δ • খنͰ୯७ͳσʔληοτʹద͍ͯ͠Δ • Preferences DataStoreͱProto DataStoreͷ2छྨ͕ଘࡏ͍ͯ͠Δ
• SharedPreferences͔ΒͷҠߦ͕Մೳ
Kotlin CoroutinesʹରԠ͍ͯ͠Δ • σʔλͷऔಘʹ fl ow • σʔλͷอଘʹsuspend
CoroutinesରԠ͞Ε͍ͯΔ༷ࢠ public interface DataStore<T> { public val data: Flow<T> public
suspend fun updateData(transform: suspend (t: T) -> T): T }
খنͰ୯७ͳσʔληοτʹ࠷దԽ • ୯७ͳσʔλͷΈΛӬଓԽ͢Δ͜ͱ • ෦ߋ৽ࢀর߹ੑΛαϙʔτ͍ͯ͠ͳ͍ • େنɾෳࡶͳσʔληοτ෦ߋ৽ɺࢀর߹ੑΛαϙʔτ͍ͨ͠ ߹RoomΛ͍·͠ΐ͏
2छྨͷDataStore • Key-ValueϖΞΛѻ͏Preferences DataStore • SharedPreferencesͷସ • ܕ͖ΦϒδΣΫτΛѻ͏Proto DataStore •
Protocol bufferΛ༻ͯ͠εΩʔϚఆٛΛ͢Δ • ܕ҆શ
SharedPreferences
SharedPreferences vs DataStore https://medium.com/androiddevelopers/introduction-to-jetpack-datastore-3dc8d74139e7
SharedPreferences͔ΒҠߦ͢ΔϝϦοτ • ඇಉظAPI͕αϙʔτ͞Ε͍ͯΔͨΊɺUIϒϩοΩϯάʹΑΔδϟϯΫ ANRͷൃੜϦεΫ͕ܰݮ͢Δ • σʔλͷҰ؏ੑɾ߹ੑ͕औΕ͍ͯΔ • ϚΠάϨʔγϣϯʹରԠ͍ͯ͠Δ
Google→։ൃऀ • ʮSharedPreferencesΛ༻ͯ͠σʔλΛอଘ͍ͯ͠Δ߹ɺ DataStoreʹҠߦ͢Δ͜ͱΛݕ౼͍ͯͩ͘͠͞ɻʯ https://developer.android.com/topic/libraries/architecture/datastore
ಋೖͯ͠ΈΔ
Preferences DataStore
ґଘؔͷՃ dependencies { implementation "androidx.datastore:datastore-preferences:1.0.0" }
Preferences DataStoreΛ࡞ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = “settings")
Preferences DataStoreΛ࡞ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = “settings") ⚠1ϑΝΠϧʹରͯ͠DataStoreͷΠϯελϯεγϯάϧτϯʹ͢Δ͜ͱ⚠
→ಉϓϩηε্ʹॴఆϑΝΠϧʹରͯ͠ΞΫςΟϒͳDataStore͕ෳ͋Δͱ σʔλʹ৮ΕΔࡍʹIllegalStateExceptionΛεϩʔ͢Δ
Preferences DataStoreΛ࡞ val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = “settings")
DataStoreͷinterfaceʢ࠶ܝʣ public interface DataStore<T> { public val data: Flow<T> public
suspend fun updateData(transform: suspend (t: T) -> T): T }
ಡΈऔΓ val COMIC_USER_IDS = stringSetPreferencesKey("comic_user_ids") val comicUserIdsFlow: Flow<Set<String>> = context.dataStore.data
.map { preferences -> preferences[COMIC_USER_ID] ?: emptySet() }
ಡΈऔΓ val COMIC_USER_IDS = stringSetPreferencesKey("comic_user_ids") val comicUserIdsFlow: Flow<Set<String>> = context.dataStore.data
.map { preferences -> preferences[COMIC_USER_ID] ?: emptySet() } booleanPreferencesKey intPreferencesKey longPreferencesKey fl oatPreferencesKey stringPreferencesKey
ॻ͖ࠐΈ val COMIC_USER_IDS = stringSetPreferencesKey("comic_user_ids") context.dataStore.edit { preferences -> preferences[COMIC_USER_IDS]
= newUserIds }
SharedPreferences→DataStoreͷҠߦ const val PREFERENCES_NAME = "..." val Context.dataStore by preferencesDataStore(
name = PREFERENCES_NAME, produceMigrations = { context -> SharedPreferencesMigration( context = context, sharedPreferencesName = PREFERENCES_NAME, // ࢦఆͨ͠keyͷΈҠߦͰ͖ΔʂʢσϑΥϧτҾMIGRATE_ALL_KEYSʣ keysToMigrate = setOf(Key.COMIC_USER_IDS) ) } )
SharedPreferences→DataStoreͷҠߦ const val PREFERENCES_NAME = "..." val Context.dataStore by preferencesDataStore(
name = PREFERENCES_NAME, produceMigrations = { context -> SharedPreferencesMigration( context = context, sharedPreferencesName = PREFERENCES_NAME, // ࢦఆͨ͠keyͷΈҠߦͰ͖ΔʂʢσϑΥϧτҾMIGRATE_ALL_KEYSʣ keysToMigrate = setOf(Key.COMIC_USER_IDS) ) } )
pixivίϛοΫͰͷӡ༻ํ๏
pixivίϛοΫͰͷํ • ৽ن࣮ͰੵۃతʹPreferences DataStoreΛ࠾༻͢Δ • طଘͷSharedPreferencesख͕ۭ͍ͨͱ͖ʹҠߦΛݕ౼ • 1िؒͷ10%Λ෦্࣭ʹࣗ༝ʹͯΒΕΔʢ10%ϧʔϧʣ
pixivίϛοΫͰͷӡ༻ • Preferences DataStoreΛѻ͏interfaceΛఆٛ • ӬଓԽ͍ͨ͠σʔλ͕͋ΕͦͷinterfaceΛܧঝ࣮ͯ͢͠Δ
DataStoreͷΠϯελϯεΛऔಘͰ͖ΔΑ͏ʹ͢Δ private const val PREFERENCES_NAME = "......" val Context.dataStore by
preferencesDataStore( name = PREFERENCES_NAME )
γϯάϧτϯͳDataStoreΛѻ͑ΔinterfaceΛఆٛ interface Preference { val context: Context val dataStore: DataStore<Preferences>
get() = context.dataStore }
ѻ͏σʔλʹԠͯ͡DataStoreͷ࣮Λ͢Δ interface ComicUserIdsPreference : Preference<Set<String>> { val userIds: Flow<Set<String>> suspend
fun add(id: String) suspend fun remove(id: String) }
ѻ͏σʔλʹ Ԡͯ͡DataStore ͷ࣮Λॻ͘ class ComicUserIdsPreferenceImpl @Inject( override val context: Context
) : ComicUserIdsPreference { override val userIds: Flow<Set<String>> get() = dataStore.data .catch { if (exception is IOException) { emit(emptyPreferences()) } else { throw exception } } .map { it[PreferencesKeys.ComicUserIds.key] ?: emptySet() } override suspend fun add(id: String) { dataStore.edit { it[PreferencesKeys.ComicUserIds.key] = it[PreferencesKeys.ComicUserIds.key].plus(id).toSet() } } override suspend fun remove(id: String) { dataStore.edit { it[PreferencesKeys.ComicUserIds.key] = it[PreferencesKeys.ComicUserIds.key].minus(id).toSet() } } } Dagger2ͰDIͯ͠·͢ ʢhilitҠߦ͍ͨ͠……ʣ
KeyΛཧ͢Δ sealed class PreferencesKeys<T>(val key: Preferences.Key<T>) { object ComicUserIds :
PreferencesKeys<Set<String>>(stringSetPreferencesKey("comic_user_ids")) ... } class PreferenceTest { @Test fun checkDuplicatedKeys() { val subClasses = PreferencesKeys::class.sealedSubclasses val nonDuplicatedKeys = subClasses.map { it.objectInstance?.key?.name }.toSet() Truth.assertThat(nonDuplicatedKeys.count()).isEqualTo(subClasses.count()) } } KeyͷॏෳΛ͙ͨΊʹsealed classͰఆٛ ॏෳνΣοΫͷςετ͔ͬ͠Γ࣮ࢪ
ಉظͰΛऔಘͨ͘͠ͳͬͨέʔε
ΞϓϦىಈ࣌ͷςʔϚө
ઃఆ͕ө͞ΕΔલʹભҠͯ͠͠·͍ ҙਤ͠ͳ͍ςʔϚ͕ͪΒ͍ͭͯ͠·͏
Կ͕ى͍ͬͯ͜Δͷ͔ • ΞϓϦىಈ࣌ʹApplicationΫϥεͰςʔϚઃఆΛऔಘͯ͠ద༻͍ͯ͠Δ • ςʔϚઃఆPreferences DataStoreͰӬଓԽ͍ͯ͠Δ • DataStoreಉظॲཧʹରԠ͍ͯ͠ͳ͍ • ΞϓϦىಈˠϗʔϜը໘ͷը໘ભҠʹςʔϚͷө͕ؒʹ߹Θͣɺσ
ϑΥϧτͷςʔϚ͕ө͞ΕΔ • ͦͷޙɺඇಉظʹө͞ΕͨςʔϚઃఆ͕ద༻͞Εͨ
Կ͕ى͍ͬͯ͜Δͷ͔ • ΞϓϦىಈ࣌ʹApplicationΫϥεͰςʔϚઃఆΛऔಘͯ͠ద༻͍ͯ͠Δ • ςʔϚઃఆPreferences DataStoreͰӬଓԽ͍ͯ͠Δ • DataStoreಉظॲཧʹରԠ͍ͯ͠ͳ͍ • ΞϓϦىಈˠϗʔϜը໘ͷը໘ભҠʹςʔϚͷө͕ؒʹ߹Θͣɺσ
ϑΥϧτͷςʔϚ͕ө͞ΕΔ • ͦͷޙɺඇಉظʹө͞ΕͨςʔϚઃఆ͕ద༻͞Εͨ
ղܾࡦ • DataStore͔ΒΛऔಘ͢ΔͷΛಉظॲཧʹ͢Δ • runBlockingͱFlow# fi rstΛۦ͢Δ • SharedPreferencesΛ͏
DataStoreͰಉظॲཧ͢Δ // ςʔϚͷద༻͕ྃ͢ΔͷΛͭͨΊʹrunBlockingͰॲཧ͢Δ runBlocking { initTheme() } private suspend fun
initTheme() { themeSettingPreference.currentThemeStatus.first().let { AppCompatDelegate.setDefaultNightMode( it.getAppTheme().convertNightModeParameter() ) } }
runBlocking͢Δͱ͖ͷҙࣄ߲ • લఏͱͯ͠ਪ͞ΕΔํ๏Ͱͳ͍ • ຖճϑΝΠϧͷI/O͕Δͱ͍͏Θ͚Ͱͳ͍ͷͰɺͦΕΛཧղͯ͠ར༻͢ΔͷΞϦ • UIεϨουΛϒϩοΩϯά͢ΔͷͰɺδϟϯΫANRͷՄೳੑ͋Δ • ैདྷͷSharedPreferencesͱಉ͡ •
ෳՕॴͰݺͼग़͢߹ࣄલʹϓϦϩʔυΛ͓ͯ͘͠ͷ͕ྑ͍ • 2ճҎ߱ͷݺͼग़͠ͰϝϞϦΩϟογϡʹώοτ͢Δ • Activity#onCreateͱ͔ͰݺͿͱΑ͍
·ͱΊ
·ͱΊ • DataStoreͱ৽͍͠ϩʔΧϧετϨʔδιϦϡʔγϣϯͰ͋Δ • Kotlin Coroutines/FlowʹରԠ͍ͯ͠Δ • SharedPreferences͔ΒͷϚΠάϨʔγϣϯՄೳ • ಉظॲཧ͕ඞཁʹͳͬͨΒrunBlocking͢Δ
͋Γ͕ͱ͏͍͟͝·ͨ͠🙇
ࢀߟจݙ • ެࣜυΩϡϝϯτΨΠυ • Introduction to Jetpack DataStore • ϓϩμΫτͰ҆શʹDataStoreҠߦ͢Δ
• SharedPreferencesɺDataStoreҠߦͯ͠ΈΑ͏