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
DataStoreを導入してみた
Search
kobaken
April 20, 2023
Technology
410
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
DroidKaigi初登壇の舞台裏
kobaken0029
0
36
AIがレビューする時代に、 Androidエンジニアは何をレビューするのか 〜 土台づくりとレビュー再設計 〜
kobaken0029
3
1.7k
In the Age of AI Code Review, What Should Android Engineers Review? - Building the Foundation & Redesigning Code Review -
kobaken0029
0
43
複数行のTextで中間省略(…)を実現する
kobaken0029
0
100
Jetpack Compose Preview実践ガイド
kobaken0029
0
190
Serializable / Parcelableとの上手な付き合い方
kobaken0029
0
290
Kotlinの好きなところ
kobaken0029
0
1.4k
Compose駆動開発のためのマルチモジュール化
kobaken0029
0
290
Epoxyを用いたレイアウト構築術
kobaken0029
1
270
Other Decks in Technology
See All in Technology
ビジネス部門からPOを迎えての半年間の進歩と課題
zozotech
PRO
0
150
[FECONF-2026] 신대륙을 향한 카카오페이증권 프론트엔드 항해기 (이주승)
l1ghthouse
0
210
Claude for Google Workspaces ポン出し/claude-for-google-workspace-slide-conversion
emiki
0
13k
Vue.jsのGitHubから学ぶ意思決定
yut0naga1_fa
1
270
Claude Codeから見る、エンジニアの世界の変容とその先
oikon48
6
4.2k
局所最適は悪ではない。壊れているのは指標です。量と質を同時に見る先行指標の設計
recruitengineers
PRO
2
1.2k
Adversary Infrastructure Hunting101
disconinja
0
160
What Is JetBrains Air
x5gtrn
PRO
0
170
AIエージェント時代、Kaggleにどう取り組むか
takanao
1
180
外国人エンジニアがVueプロトタイプで仕様を合わせている話 / Vue Fes Japan 2026 LT
eluwing
0
170
AWSDirectConnectの構築の進め方を整理してみた
masa_my
1
200
あなたのDartコードは丸見えかもしれない ~reFlutterで暴くFlutterアプリの中身と多層防御~
zozotech
PRO
0
160
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.9k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
550
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
[RailsConf 2023] Rails as a piece of cake
palkan
59
7.1k
First, design no harm
axbom
PRO
2
1.4k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
380
The SEO Collaboration Effect
kristinabergwall1
1
600
Chasing Engaging Ingredients in Design
codingconduct
0
370
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
18k
Embracing the Ebb and Flow
colly
88
5.2k
Thoughts on Productivity
jonyablonski
76
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Ҡߦͯ͠ΈΑ͏