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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
kobaken
April 20, 2023
Technology
400
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
AIがレビューする時代に、 Androidエンジニアは何をレビューするのか 〜 土台づくりとレビュー再設計 〜
kobaken0029
3
670
In the Age of AI Code Review, What Should Android Engineers Review? - Building the Foundation & Redesigning Code Review -
kobaken0029
0
29
複数行のTextで中間省略(…)を実現する
kobaken0029
0
85
Jetpack Compose Preview実践ガイド
kobaken0029
0
170
Serializable / Parcelableとの上手な付き合い方
kobaken0029
0
230
Kotlinの好きなところ
kobaken0029
0
1.4k
Compose駆動開発のためのマルチモジュール化
kobaken0029
0
280
Epoxyを用いたレイアウト構築術
kobaken0029
1
270
Androidエンジニアが1週間でiOSアプリ開発を学び、1ヶ月で大規模アプリ開発にJOINした話
kobaken0029
0
3.9k
Other Decks in Technology
See All in Technology
書籍『生成AIの安全性入門』の入門
wataoka
0
210
【試作】IoT x AIエージェント
happysamurai294
0
100
Azure Cost Management の FOCUS コストデータを迷わず読むための“3つの軸”
tetsuyaooooo
0
140
【Oracle AI Spotlight ウェビナー】AWSか、Azureか、Google Cloudか。その議論にオラクルを含める意義。
oracle4engineer
PRO
2
220
いま、生成AIにKaggleをどこまで 任せられるか — ROGIIコンペでの進め方とTips
k951286
2
1.3k
スクラムで身についていた動き方を、XPで捉え直してみた
codmoninc
1
190
DINO-EdgeQuery:Edge-First Polygon Decoding for Building Footprint Extraction from Satellite Imagery
lehupa
0
140
Nav2、Nav3 ... はたまた自作?〜 作って理解する Nav3 の設計意図 〜 / Nav2, Nav3 ... or Build Your Own? — Understanding Nav3's design intent by building it from scratch
yanzm
0
200
Azure App Service / Container Apps の組み込み認証
kuniteru
0
190
AI-DLCって実際どう? 〜聞きたいこと全部聞いてみる〜
news_it_enj
0
220
Genie Code ワークショップ 応用編 / Genie-Code-Workshop-advanced
databricksjapan
PRO
0
310
AndroidでHDRメディアを「壊さずに」扱う
chigichan24
0
380
Featured
See All Featured
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
YesSQL, Process and Tooling at Scale
rocio
174
15k
A better future with KSS
kneath
240
18k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
370
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.2k
Tell your own story through comics
letsgokoyo
1
1.1k
Google's AI Overviews - The New Search
badams
0
1.6k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
270
Why Our Code Smells
bkeepers
PRO
340
58k
Darren the Foodie - Storyboard
khoart
PRO
3
3.9k
The Cult of Friendly URLs
andyhume
79
7k
Bash Introduction
62gerente
615
220k
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Ҡߦͯ͠ΈΑ͏