跳转到正文
YGJoon
返回

Android Kotlin DataStore 配置与迁移

编辑文章

DataStore 简介

Preferences DataStore

val Context.settingsDataStore by preferencesDataStore(name = "settings")

object SettingsKeys {
    val DARK_MODE = booleanPreferencesKey("dark_mode")
}

suspend fun setDarkMode(enabled: Boolean, context: Context) {
    context.settingsDataStore.edit { prefs ->
        prefs[SettingsKeys.DARK_MODE] = enabled
    }
}

val darkModeFlow: Flow<Boolean> = context.settingsDataStore.data
    .map { prefs -> prefs[SettingsKeys.DARK_MODE] ?: false }

Proto DataStore

1. 定义 proto

user_prefs.proto

syntax = "proto3";

option java_package = "com.example.datastore";

message UserPreferences {
  string userId = 1;
  string locale = 2;
}

2. 配置 Gradle

plugins {
    id("com.google.protobuf") version "0.9.4"
}

protobuf {
    protoc { artifact = "com.google.protobuf:protoc:3.25.3" }
    generateProtoTasks {
        all().forEach { task ->
            task.builtins {
                create("java")
                create("kotlin")
            }
        }
    }
}

3. 使用 ProtoDataStore

val Context.userPreferencesDataStore: DataStore<UserPreferences> by dataStore(
    fileName = "user_prefs.pb",
    serializer = UserPreferencesSerializer
)

object UserPreferencesSerializer : Serializer<UserPreferences> {
    override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance()

    override suspend fun readFrom(input: InputStream): UserPreferences =
        try {
            UserPreferences.parseFrom(input)
        } catch (e: InvalidProtocolBufferException) {
            throw CorruptionException("Cannot read proto.", e)
        }

    override suspend fun writeTo(t: UserPreferences, output: OutputStream) = t.writeTo(output)
}

SharedPreferences 迁移

val Context.migratedDataStore by dataStore(
    fileName = "settings.pb",
    serializer = SettingsSerializer,
    produceMigrations = { context ->
        listOf(SharedPreferencesMigration(context, "old_settings"))
    }
)

迁移逻辑可在 SharedPreferencesMigration.migrate 中自定义。

多模块集成

@Module
@InstallIn(SingletonComponent::class)
object DataStoreModule {
    @Provides
    fun provideUserPreferences(@ApplicationContext context: Context): DataStore<UserPreferences> {
        return context.userPreferencesDataStore
    }
}

测试与调试

@RunWith(AndroidJUnit4::class)
class DataStoreTest {
    @get:Rule
    val tmpFolder = TemporaryFolder()

    private val testContext = ApplicationProvider.getApplicationContext<Context>()
        .createDeviceProtectedStorageContext()

    @Test
    fun writeAndRead() = runTest {
        val store = PreferenceDataStoreFactory.create(
            scope = TestScope(UnconfinedTestDispatcher()),
            produceFile = { tmpFolder.newFile("settings.preferences_pb") }
        )
        store.edit { it[booleanPreferencesKey("flag")] = true }
        assertTrue(store.data.first()[booleanPreferencesKey("flag")] == true)
    }
}

常见问题

问题原因解决方案
数据读取卡住未消费 Flow使用 collect 或 first 获取数据
Proto 解析失败序列化字段缺失升级 schema 并提供默认值
UI 不更新未切换到主线程flowOn(Dispatchers.IO) + collect 在 Main

总结

  1. DataStore 更适合现代 Kotlin 异步流程,与 Flow、协程配合紧密。
  2. Preferences 模式简单易用,Proto 模式提供类型安全与扩展性。
  3. 合理设计迁移方案,可平滑替代旧的 SharedPreferences,保障数据连续性。

编辑文章
分享这篇文章:

上一篇
Android Kotlin Flow 数据流与状态管理
下一篇
Android Kotlin Compose 导航与深链