我正在使用 datastore-preferences:1.0.0-alpha01 并且当数据存储值更新时我似乎无法恢复事件。我一直试图在片段和父活动中观察它,结果相同。当我创建 DataStore 的实例并观察值流时,我在声明观察者后立即收到一个事件(注册观察者后似乎很常见)。这将表明流正在运行,并且观察者正在按预期接收事件。然后我在同一个观察者内部更新首选项的值,该观察者从不接收事件。
我使用这篇文章作为参考https://medium.com/scalereal/hello-datastore-bye-sharedpreferences-android-f46c610b81d5,它与我用来比较我的实现的 repo 一起使用。显然,这个是有效的。https://github.com/PatilShreyas/DataStoreExample
数据存储实用程序
class DataStoreUtils(context: Context) {
companion object {
private const val TAG = "DataStoreUtils"
}
private val dataStore = context.createDataStore(name = Constants.PrefName.APP_PREFS)
suspend fun setString(prefKey: String, value: String) {
Log.d(TAG, "Setting $prefKey to $value")
dataStore.edit { it[preferencesKey<String>(prefKey)] = value }
}
suspend fun getString(prefKey: String): String? {
return dataStore.data.map { it[preferencesKey<String>(prefKey)] ?: return@map null }.first()
}
val usernameFlow: Flow<String?> = dataStore.data
.catch {
if (it is IOException) {
it.printStackTrace() …Run Code Online (Sandbox Code Playgroud) android android-lifecycle kotlin-flow android-jetpack-datastore
由于当共享首选项数据超过 1428.51-kb 时会出现内存异常,因此推荐的 Android Jetpack DataStore 是否具有相同的内存限制?
由于 DataStore 使用带有协议缓冲区的类型化对象,因此在内存方面会有优势吗?
我创建了一个composable名为 ResolveAuth. ResolveAuth 是用户在 Splash 后打开应用程序时的第一个屏幕。它所做的只是检查数据存储中是否存在电子邮件。如果是,则重定向到主屏幕,如果不是,则重定向到教程屏幕
这是我的composable代码viewmodel
@Composable
fun ResolveAuth(resolveAuthViewModel: ResolveAuthViewModel, navController: NavController) {
Scaffold(content = {
ProgressBar()
when {
resolveAuthViewModel.userEmail.value != "" -> {
navController.navigate(Screen.Main.route) {
popUpTo(0)
}
resolveAuthViewModel.userEmail.value = null
}
resolveAuthViewModel.userEmail.value == "" -> {
navController.navigate(Screen.Tutorial.route) {
popUpTo(0)
}
resolveAuthViewModel.userEmail.value = null
}
}
})
}
@HiltViewModel
class ResolveAuthViewModel @Inject constructor(
private val dataStoreManager: DataStoreManager): ViewModel(){
val userEmail = MutableLiveData<String>()
init {
viewModelScope.launch{
val job = async {dataStoreManager.email.first()}
val email = job.await() …Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-navigation android-jetpack-compose android-jetpack-datastore
核心问题是我想为子消息设置默认值。由于 proto3 不允许在 proto 文件中设置默认值。
示例 Protobuf
message Person {
int32 age = 1;
repeated Doggo doggos = 2;
}
message Doggo {
int32 rating = 1;
}
Run Code Online (Sandbox Code Playgroud)
串行器示例
object PersonSerializer : Serializer<Person> {
override val defaultValue: Person =
Person.getDefaultInstance().toBuilder()
.setAge(1)
.build()
override suspend fun readFrom(input: InputStream): Person {
try {
return Person.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
override suspend fun writeTo(t: Person, output: OutputStream) {
t.writeTo(output)
}
}
Run Code Online (Sandbox Code Playgroud)
这可以将所有 Person 的默认年龄设置为 1。标准值为0。 …
我将选项菜单复选框项的状态(选中/未选中)保存在 Jetpack DataStore 中:
override fun onOptionsItemSelected(item: MenuItem) =
when (item.itemId) {
R.id.action_hide_completed_tasks -> {
item.isChecked = !item.isChecked
viewModel.hideCompleted(item.isChecked)
true
}
else -> super.onOptionsItemSelected(item)
}
Run Code Online (Sandbox Code Playgroud)
它调用这个方法:
suspend fun updateHideCompleted(hideCompleted: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.HIDE_COMPLETED] = hideCompleted
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我的片段出现在屏幕上时,我想恢复此复选框状态。但由于 Jetpack DataStore 以 的形式提供用户首选项Flow,这感觉有点 hacky。我从中收集并使用运算符仅执行一次,因为此后我不再需要从首选项中更新复选框,当我单击选项菜单项时状态会自动更改Flow:onPrepareOptionsMenutake(1)
override fun onPrepareOptionsMenu(menu: Menu) {
lifecycleScope.launch {
viewModel.preferencesFlow
.take(1) // I want this to update only when the fragment comes onto the screen, afterward it's unnecessary
.collect { …Run Code Online (Sandbox Code Playgroud) android android-architecture-components android-jetpack android-jetpack-datastore
EDIT: I NEED TO PASS A CONTEXT AS A PARAMETER TO THE CLASS
(DataStore是repository同一个班级,不要混淆)
我有两项活动,A和B一项repository。ActivityA打开 Activity B,ActivityB将数据保存在repository(DataStoreAndroid jetpack 的一部分)中。
我LiveData在这两个活动中都使用来观察DataStore.
更新DataStorefrom 活动中的新值后B,LiveData活动中的B会按预期获取新的更新值。但是,当我返回活动时,A会LiveData获取旧数据(期望新的更新数据)。
我意识到这种情况正在发生,因为我repository在这两个活动中创建了两个实例。
我怎样才能只创建该类的一个实例repository并在这两个活动中使用它?如果有更好的方法来做到这一点,那么该解决方案也受到欢迎。
singleton android repository kotlin android-jetpack-datastore
我正在使用 jetpack 数据存储来存储用户首选项。我要完美地检索数据,但是当我尝试在数据存储中写入数据时,它给了我一个错误。附加了错误,它是一个未找到的类异常我不知道依赖项是否有问题,或者只是我有问题。
这是我用来处理数据存储的类
enum class UiMode {
LIGHT,DARK
}
class DarkModeManager (context : Context){
private val dataStore = context.createDataStore("settings")
val uiModeFlow: Flow<UiMode> = dataStore.data
.catch {
if (it is IOException) {
it.printStackTrace()
emit(emptyPreferences())
} else {
throw it
}
}
.map { preference ->
val isDarkMode = preference[IS_DARK_MODE] ?: false
when (isDarkMode) {
true -> UiMode.DARK
false -> UiMode.LIGHT
}
}
suspend fun setUiMode(uiMode: UiMode) {
dataStore.edit { preferences ->
preferences[IS_DARK_MODE] = when (uiMode) {
UiMode.LIGHT -> false
UiMode.DARK …Run Code Online (Sandbox Code Playgroud) 我在使用 OutlinedTextField 和连接到存储用户设置的数据存储时遇到一些问题。数据已保存并正确读取。但是,更新显示和光标似乎存在问题。当我输入文本时(可能是因为我打字太快),光标向左跳一位。这会破坏输入并且用户体验非常糟糕。
到目前为止我还没有找到解决问题的方法。你知道它可能是什么吗?非常感谢!
这就是我用于 OutlinedTextField 的代码:
OutlinedTextField(
value = dataStoreState.nickname,
onValueChange = {
CoroutineScope(Dispatchers.IO).launch {
viewModel.saveToDataStore(dataStoreState.copy(nickname = it))
}
},
label = { Text(stringResource(id = R.string.enterNickname)) },
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
modifier = Modifier.fillMaxWidth()
)
Run Code Online (Sandbox Code Playgroud)
我尝试将文本值缓存在另一个变量中,但没有效果。然后我尝试更改保存功能,但我在这里也找不到正确的解决方案。
我已经使用数据存储很长时间了。今天我必须读取主线程中的值。查看文档后,我决定使用runblocking。我创建了一个长值,其名称为lastInsertedId。
我在片段 A 中读取了lastInsertedId,然后导航到片段B,并且我正在更改lastInsertedId 的值。当我回到片段 A 时,我再次阅读了lastInsertedId。但lastInsertedId的值仍然相同。实际上它的值正在改变,但我无法读取它的最后一个值。
我认为这是因为碎片A没有被破坏。只有 onDestroyView 是从 onCreateView 调用并创建的。我想要的是只要我想在主线程中访问lastInsertedID 的当前值。
当我将其创建为变量时,它总是返回相同的值。但当我将它转换为函数时,它运行良好。但我不认为这是最佳实践。访问此值的最佳方式是什么?谢谢。
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "main")
@Singleton
class DataStoreManager @Inject constructor(@ApplicationContext appContext: Context) {
private val mainDataStore = appContext.dataStore
suspend fun setLastInsertedId(lastId: Long) {
mainDataStore.edit { main ->
main[LAST_INSERTED_ID] = lastId
}
}
// Returns always the same value
val lastInsertedId: Long = runBlocking {
mainDataStore.data.map { preferences ->
preferences[LAST_INSERTED_ID] ?: 0
}.first()
}
// Returns as expected
fun lastInsertedId(): Long = …Run Code Online (Sandbox Code Playgroud) 只是测试Preferences DataStore并发现提供的Flow输出不会发出相同的值,我的设置如下:
数据存储实用程序类:
object DataStore {
private val Context.settings by preferencesDataStore("settings")
suspend fun saveBoolean(context: Context, keyResId: Int, value: Boolean) {
val key = booleanPreferencesKey(context.getString(keyResId))
context.settings.edit {
it[key] = value
}
}
fun getBooleanFlow(context: Context, keyResId: Int, defaultValueResId: Int): Flow<Boolean> {
val key = booleanPreferencesKey(context.getString(keyResId))
val defaultValue = context.resources.getBoolean(defaultValueResId)
return context.settings.data.map {
it[key] ?: defaultValue
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型类:
class FirstViewModel(application: Application) : AndroidViewModel(application) {
private val uiScope = viewModelScope
val isUpdateAvailable = DataStore.getBooleanFlow(
getApplication(), R.string.is_update_available_key, R.bool.is_update_available_default …Run Code Online (Sandbox Code Playgroud) android android-preferences sharedpreferences kotlin-flow android-jetpack-datastore
android ×9
kotlin ×4
kotlin-flow ×2
android-architecture-components ×1
repository ×1
singleton ×1