Preferences DataStoreの使い方を、まとめます。
DataStoreはPreferencesとProto DataStoreの2つがあります。
Preferences DataStoreは、データの識別子にキー(文字列)を使うタイプです。
手軽・簡素で、Proto DataStoreに比べて、使い勝手がよいです。
あえて、Proto DataStoreを使う理由が無いのであれば、Preferences DataStoreで十分です。
※環境:Android Studio Narwhal 3 Feature Drop | 2025.1.3
androidx.datastore:datastore-preferences:1.1.7
目次
Preferences DataStore
DataStoreはデータを「識別子と値のペア」で記録します。

Preferences DataStoreは、キー(文字列)を識別子にしたDataStoreです。
キーを指定して、データのアクセスを行います。

データはPreferencesクラス内にPairクラスで格納されています。
環境設定
build.gradleへDataStoreのライブラリを指定します。
※ライブラリについては「ライブラリ>DataStore」を参照
plugins {
...
}
android {
...
}
dependencies {
implementation("androidx.datastore:datastore-preferences:1.1.7")
...
}
DataStoreの例
アプリの設定(状態)を保存する例です。
ポイントは4つです。
- (1)DataStoreインスタンスの作成
- (2)キーの定義
- (3)コルーチンでデータの読み出し
- (4)コルーチンでデータの書き込み
// ↓↓ (1)DataStoreインスタンスの作成
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
val APP_STATE0 = intPreferencesKey("app_state0") // (2)キーの定義
val APP_STATE1 = intPreferencesKey("app_state1")
val APP_STATE2 = intPreferencesKey("app_state2")
private const val UNDEFINE = -1
class MainActivity : ComponentActivity() {
private var state0: Int = UNDEFINE
private var state1: Int = UNDEFINE
private var state2: Int = UNDEFINE
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val state0Flow: Flow<Int> = dataStore.data // (3)データの読み出し
.map { preferences ->
preferences[APP_STATE0] ?: 1000
}
// 常に更新、通信経路を維持(スレッドをブロックしない)
lifecycleScope.launch {
state0Flow.collect { state0 = it }
}
val state1Flow: Flow<Int> = dataStore.data
.map { preferences ->
preferences[APP_STATE1] ?: 1100
}
// 実行で更新(スレッドをブロックしない)
lifecycleScope.launch(Dispatchers.Default) {
state1 = state1Flow.first()
}
val state2Flow: Flow<Int> = dataStore.data
.map { preferences ->
preferences[APP_STATE2] ?: 1200
}
// 実行で更新(スレッドをブロックする)
state2 = runBlocking { state2Flow.first() }
setContent { ... }
}
}
...
lifecycleScope.launch(Dispatchers.Default) { // (4)データの書き込み
dataStore.edit { settings ->
settings[APP_STATE0] = _nextState0
}
}
...
lifecycleScope.launch(Dispatchers.Default) { // 記述はState0と同じ
dataStore.edit { settings ->
settings[APP_STATE1] = _nextState1
}
}
...
lifecycleScope.launch(Dispatchers.Default) { // 記述はState0と同じ
dataStore.edit { settings ->
settings[APP_STATE2] = _nextState2
}
}
...
(1)DataStoreインスタンスの作成
DataStoreインスタンスを作成します。
この時、データの書き込み先のファイル名を指定します。
// ↓↓ (1)DataStoreインスタンスの作成 val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") //
インスタンスはアプリケーション内に1つだけ作成します。ここでは、Contextインスタンスの拡張プロパティに代入しています。それにより、「アプリケーション内に1つ」を実現しています。
preferencesDataStore( )は引数にファイル名を伴って、DataStoreインスタンスを返します。委譲プロパティなので、プロパティへの代入は初回の参照時です。
ちなみに、書き込み先のファイルは以下の場所にあります。
# pwd /data/user/0/パッケージ名/files/datastore # ls -l total 16 -rw------- 1 u0_a174 u0_a174 19 2025-09-08 01:02 settings.preferences_pb
場所はサンドボックス内(アプリ固有の領域)です。
(2)キーの定義
キーの定義を行います。
val APP_STATE0 = intPreferencesKey("app_state0") // (2)キーの定義
val APP_STATE1 = intPreferencesKey("app_state1")
val APP_STATE2 = intPreferencesKey("app_state2")
キーの定義で使用するxxxPreferencesKey( )関数は、キーへデータ型を関連付ける役割も持ちます。
サンプルはInt型に関連付けしています。
その他、次のような関数が準備されています。
public fun intPreferencesKey(name: String): Preferences.Key<Int> public fun doublePreferencesKey(name: String): Preferences.Key<Double> public fun stringPreferencesKey(name: String): Preferences.Key<String> public fun booleanPreferencesKey(name: String): Preferences.Key<Boolean> public fun floatPreferencesKey(name: String): Preferences.Key<Float> public fun longPreferencesKey(name: String): Preferences.Key<Long> public fun stringSetPreferencesKey(name: String): Preferences.Key<Set<String>> public fun byteArrayPreferencesKey(name: String): Preferences.Key<ByteArray> //
Preferences DataStoreは関数の準備されているデータ型をサポートします。
(3)コルーチンでデータの読み出し
Flow経由でReferencesオブジェクトを取得し、データの読み出しを行います。

ストリームデータはReferencesオブジェクトです。
ファイルが更新される度に、データが送信される点に注意して下さい。
受信したReferencesオブジェクトから、キーを使って、データを読み出します。
Flow.map(中間関数)を使って、Flowを個別データへ割り振り直すと便利です。
※中間関数については「Coroutine:Flowのストリームデータ変更(中間演算)」を参照
collectを使って
collectを使って受信する例です。
Flowの通信経路は維持されます。受信データが無い場合は、スレッドを休止して待ちます。
val state0Flow: Flow<Int> = dataStore.data // (3)データの読み出し
.map { preferences ->
preferences[APP_STATE0] ?: 1000
}
// 常に更新、通信経路を維持(スレッドをブロックしない)
lifecycleScope.launch {
state0Flow.collect { state0 = it }
}
経路が維持される限り、「ファイルの更新⇒データの送信⇒データの受信⇒設定(状態)の更新」を繰り返すので、アプリの設定は常に最新の値になります。
キー対応する値がファイルに存在しなければ、nullを返します。nullの場合は初期値に置き換えています。
first()を使って
firstを使って受信する例です。
最初のデータを受け取るまで待機し、受信後キャンセル信号を送信機へ送ります。
ですので、一回限りの読み出しで、Flowの通信経路は閉じられます。
val state1Flow: Flow<Int> = dataStore.data
.map { preferences ->
preferences[APP_STATE1] ?: 1100
}
// 実行で更新(スレッドをブロックしない)
lifecycleScope.launch(Dispatchers.Default) {
state1 = state1Flow.first()
}
ちなみに、Flowはホットストリームなので、Flowの通信経路はfirstを実行する度に作成されます。
runBlockingを使って
runBlockingを使って受信する例です。
スレッドはデータを受信するまでブロックされます。サンプルの場合はMainスレッドがブロックされます。
val state2Flow: Flow<Int> = dataStore.data
.map { preferences ->
preferences[APP_STATE2] ?: 1200
}
// 実行で更新(スレッドをブロックする)
state2 = runBlocking { state2Flow.first() }
ANR(Application Not Responding)を引き起こす可能性があるので、お勧めしません。
(4)コルーチンでデータの書き込み
editを使うと、MutablePreferences(変更可能なPreferences)が取り出せます。
取り出したMutablePreferencesオブジェクトへ、キーを使って、データを書き込みます。
lifecycleScope.launch(Dispatchers.Default) { // (4)データの書き込み
dataStore.edit { settings ->
settings[APP_STATE0] = _nextState0
}
}
関連記事:
