だいぶ前に書いたSharedPreferences は扱いやすいけど、移行を検討しろって言われているので試してみました
https://developer.android.com/topic/libraries/architecture/datastore
Preferences DataStore
実装は↑通りにすればokなのでHilt適応版
とりあえずサクッとベースのコピペコード
[versions]
datastore = "1.1.1"
hilt = "2.52"
hiltNavigationCompose = "1.2.0"
com-google-devtools-ksp = "2.0.0-1.0.24"
[libraries]
androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
[plugins]
com-google-devtools-ksp = {id = "com.google.devtools.ksp", version.ref = "com-google-devtools-ksp" }
com-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
plugins {
alias(libs.plugins.com.google.devtools.ksp) apply false
alias(libs.plugins.com.google.dagger.hilt.android) apply false
}
plugins {
alias(libs.plugins.com.google.devtools.ksp)
alias(libs.plugins.com.google.dagger.hilt.android)
}
dependencies {
implementation(libs.androidx.datastore.preferences)
implementation(libs.hilt.android)
ksp(libs.hilt.android.compiler)
implementation(libs.androidx.hilt.navigation.compose)
}
/**
* ```AndroidManifest.xml
* <application
* android:name=".Application"
* ```
*/
@HiltAndroidApp
class Application : android.app.Application() {
override fun onCreate() {
super.onCreate()
}
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
...
setContent {
...
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Screen(Modifier.padding(innerPadding))
}
...
Preferences DataStore 定義
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
class AppDataPreferences @Inject constructor(
private val dataStore: DataStore<Preferences>
) {
val count = dataStore.data
.catch {
emit(emptyPreferences())
}
.map { preferences ->
preferences[KEY_COUNT] ?: 0
}
suspend fun getCount(): Result<Int> = Result.runCatching {
withContext(Dispatchers.IO) {
count.first()
}
}
suspend fun setCount(count: Int) = Result.runCatching {
withContext(Dispatchers.IO) {
dataStore.edit { preferences ->
preferences[KEY_COUNT] = count
}
}
}
private companion object {
val KEY_COUNT = intPreferencesKey(
name = "count"
)
}
}
@Module
@InstallIn(SingletonComponent::class)
class DateStoreModule {
@Singleton
@Provides
fun provideAppPreferences(
@ApplicationContext context: Context,
) = AppDataPreferences(context.dataStore)
}
確認用UI
ボタン押したらカウント増えていきます
アプリを落としても値が保持されていたらok
@HiltViewModel
class MainViewModel @Inject constructor(
private val appDataPreferences: AppDataPreferences,
) : ViewModel() {
val count = appDataPreferences.count.stateIn(viewModelScope, SharingStarted.Eagerly, 0)
fun incrementCount() = viewModelScope.launch {
val count = appDataPreferences.getCount().getOrDefault(0)
appDataPreferences.setCount(count + 1)
}
}
@Composable
fun Screen(
modifier: Modifier = Modifier,
vm: MainViewModel = hiltViewModel()
) {
val count by vm.count.collectAsState()
Column(modifier) {
Button(onClick = {
vm.incrementCount()
}) {
Text(count.toString())
}
}
}
Proto DataStore (protobuf)
調査中…相変わらず情報が分散していてサクッと実装できないw
kotlinにすると自動生成したファイルでエラーが出る…
protobuf-bomも機能してない?
[versions]
datastore = "1.1.1"
[libraries]
androidx-datastore = { group = "androidx.datastore", name = "datastore", version.ref = "datastore" }
dependencies {
implementation(libs.androidx.datastore)
}
android devの説明に合わせて.protoを追加
syntax = "proto3";
package com.xxx;
option java_multiple_files = true;
message Settings {
int32 example_counter = 1;
}
.protoのpluginはJetBrins製のでいいか
.protoの扱い方はcodelabにあります
https://developer.android.com/codelabs/android-proto-datastore#0
ここではjavaで出力だけどkotlinもサポートしてます
https://github.com/protocolbuffers/protobuf/blob/main/java/README.md
pluginのバージョンなどはこちら
https://mvnrepository.com/artifact/com.google.protobuf/protoc
[versions]
protobufKotlinLite = "4.28.3"
protobuf = "0.9.4"
[libraries]
protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobufKotlinLite" }
[plugins]
com-google-protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }
plugins {
alias(libs.plugins.com.google.protobuf)
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:4.28.3"
}
generateProtoTasks {
all().forEach {
it.builtins {
create("kotlin") {
option("lite")
}
}
}
}
}
Proto DataStore (kotlin serialization)
こっちの方が断然使いやすいような…devloperでなぜ直接紹介しない…
https://android-developers-jp.googleblog.com/2021/04/using-datastore-with-kotlin-serialization.html
main/protoに.protoの代わりにjsonを置いたりする必要もないです
@Serializable
data class AppData(
val count: Int = 0
)
// 🙅♂️import kotlinx.serialization.Serializer
// 🙆♂️import androidx.datastore.core.Serializer
object AppDataSerializer : Serializer<AppData> {
override val defaultValue = AppData()
override suspend fun readFrom(input: InputStream): AppData {
try {
return Json.decodeFromString(AppData.serializer(), input.readBytes().decodeToString())
} catch (serialization: SerializationException) {
throw CorruptionException("Unable to read UserPrefs", serialization)
}
}
override suspend fun writeTo(t: AppData, output: OutputStream) {
withContext(Dispatchers.IO) {
output.write(Json.encodeToString(AppData.serializer(), t).encodeToByteArray())
}
}
}
val Context.dataStore by dataStore("appData.json", serializer = AppDataSerializer)
@Composable
fun Screen(modifier: Modifier = Modifier) {
val context = LocalContext.current
val appData by context.dataStore.data.collectAsState(AppData())
suspend fun setCount(count: Int) = context.dataStore.updateData { it.copy(count) }
val scope = rememberCoroutineScope()
Column(modifier) {
Button(onClick = {
scope.launch {
setCount(appData.count + 1)
}
}) {
Text("${appData.count}")
}
}
}
Android Studio Ladybug 2024.2.1 Patch 2 built on October 25, 2024