〈kotlin〉Preferences DataStore
Android studioでkotlinを使ってAndroidアプリ作成の勉強中。
だいぶ前に小さいデータを保存しておくSharedPreferencesの記事を書いたが、
「Jetpack DataStoreに移行することを検討してください」となっていて、
以下のcodelabを元にPreferences DataStoreについて勉強したのでまとめてみる。
サンプルコードも以下のcodelabから。
Preferences DataStore | Android Developers
Jetpack DataStore
シンプルな小規模なデータを非同期で安全に保存できる。
以下の2種類がある。
Preferences DataStore | スキーマを定義せず、キーを使用してデータを保存・アクセスする。 データがkey-valueで保存できるほどシンプルな場合簡単に保存できる。 |
---|---|
Proto DataStore | プロトコル バッファを使用してスキーマを定義してデータを保存する。 設定が必要だがタイプセーフで高速。 |
今回はPreferences DataStoreについてのみを書く。
Preferences DataStore
設定
dependencies {
implementation("androidx.datastore:datastore-preferences:1.0.0")
}
DataStoreを作成
Kotlinファイルの最上位でpreferencesDataStoreデリゲートを使用してDataStoreインスタンスを作成し、
アプリの他の部分ではこのプロパティを介してアクセスする。
これによりDataStoreをシングルトンで保持する。
preferencesDataStoreのnameにはDataStoreの名前を設定する。
private const val LAYOUT_PREFERENCES_NAME = "layout_preferences" val Context.dataStore :DataStore<Preferences> by preferencesDataStore( name = LAYOUT_PREFERENCES_NAME )
この先のコードはSettingsDataStoreクラスとして記述していく。
保存するキーの定義
スキーマを定義しないので、対応するキー型の関数を使用して保存するキーと値の型を定義する。
class SettingsDataStore(context: Context) { private val IS_LINEAR_LAYOUT_MANAGER = booleanPreferencesKey("is_linear_layout_manager") //is_linear_layout_managerにboolean型の値を保存 }
書き込み
DataStore内のデータをトランザクションとして更新するsuspend関数 edit()を使用して、
上記で定義したキーに対して値を保存する。
edit関数にはtransform パラメータがあり、ここに値を更新するコードブロックを渡す。
class SettingsDataStore(context: Context) { private val IS_LINEAR_LAYOUT_MANAGER = booleanPreferencesKey("is_linear_layout_manager") suspend fun saveLayoutToPreferencesStore(isLinearLayoutManager:Boolean, context: Context){ context.dataStore.edit {preferences -> preferences[IS_LINEAR_LAYOUT_MANAGER] = isLinearLayoutManager } } }
読み込み
DataStoreは設定が変更されるたびに出力される Flow<Preferences>に保存されたデータを公開する。
ここからキーを使用して目的の値を取り出す。
DataStore がファイルに対してデータを読み書きする際に、
データへのアクセス時に IOExceptions が発生することがあるためキャッチしておくと良い。
class SettingsDataStore(context: Context) { private val IS_LINEAR_LAYOUT_MANAGER = booleanPreferencesKey("is_linear_layout_manager") val preferenceFlow : Flow<Boolean> = context.dataStore.data .catch { if (it is IOException){ it.printStackTrace() emit(emptyPreferences()) }else{ throw it } } .map { preferences -> preferences[IS_LINEAR_LAYOUT_MANAGER] ?: true //初回は空のため、デフォルト値を渡しておく } }
DataStoreを使う
class LetterListFragment : Fragment() { private lateinit var settingsDataStore: SettingsDataStore ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... settingsDataStore = SettingsDataStore(requireContext()) settingsDataStore.preferenceFlow.asLiveData().observe(viewLifecycleOwner) { value -> //Flowで保存されているのでLiveDataに変換して監視できる //変更時にしたい処理あれば書く } } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.action_switch_layout -> { ... //コルーチンで呼び出す lifecycleScope.launch{ settingsDataStore.saveLayoutToPreferencesStore(isLinearLayoutManager, requireContext()) } return true } else -> super.onOptionsItemSelected(item) } } }
参考
アプリ アーキテクチャ: データレイヤー - DataStore - デベロッパー向け Android | Android デベロッパー | Android Developers