Jetpack 撰写数据存储不断重组屏幕
Jetpack compose data store keeps recomposing screen
我正在使用 Jetpack Compose 从共享首选项迁移到数据存储。一切正常(数据已保存并可以成功撤回)。但是,无论何时检索数据,可组合项都会不断地重新组合。我正在使用 MVVM 架构,下面是我如何实现数据存储。
下面是在我的AppModule.kt
中声明的
SingletonComponent中的App模块
@Provides
@Singleton
fun provideUserPreferenceRepository(@ApplicationContext context: Context):
UserPreferencesRepository = UserPreferencesRepositoryImpl(context)
然后这是我的 ViewModel:
@HiltViewModel
class StoredUserViewModel @Inject constructor(
private val _getUserDataUseCase: GetUserDataUseCase
): ViewModel() {
private val _state = mutableStateOf(UserState())
val state: State<UserState> = _state
fun getUser(){
_getUserDataUseCase().onEach { result ->
val name = result.name
val token = result.api_token
_state.value = UserState(user = UserPreferences(name, agentCode, token, balance))
}.launchIn(viewModelScope)
}}
最后,这是我的存储库实现:
class UserPreferencesRepositoryImpl @Inject constructor(
private val context: Context
): UserPreferencesRepository {
private val Context.dataStore by preferencesDataStore(name = "user_preferences")
}
private object Keys {
val fullName = stringPreferencesKey("full_name")
val api_token = stringPreferencesKey("api_token")
}
private inline val Preferences.fullName get() = this[Keys.fullName] ?: ""
private inline val Preferences.apiToken get() = this[Keys.api_token] ?: ""
override val userPreferences: Flow<UserPreferences> = context.dataStore.data.catch{
// throws an IOException when an error is encountered when reading data
if (it is IOException) {
emit(emptyPreferences())
} else {
throw it
}
}.map { preferences ->
UserPreferences(name = preferences.fullName, api_token = preferences.apiToken)
}.distinctUntilChanged()
我不知道是什么导致可组合项重组。下面是可组合项:
@Composable
fun LoginScreen(
navController: NavController,
userViewModel: StoredUserViewModel = hiltViewModel()
) {
Log.v("LOGIN_SCREEN", "CALLED!")
userViewModel.getUser()
}
如果有人能告诉我哪里做错了请赐教。我尝试更改 AppModule 中 UserPreferencesRepository 的实现,但没有成功。
下面是UseState.kt这只是一个数据class
data class UserState(
val user: UserPreferences? = null
)
下面是UserPreferences.kt
data class UserPreferences(val name: String, val api_token: String)
这是预期的行为:您在每次重组时调用 getUser
。
@Composable
函数是一个视图构建器,应该没有副作用。
相反,您可以使用特殊的副作用函数,例如 LaunchedEffect
,它只会启动一次作业,直到它从视图树中删除或 key
参数被更改:
LaunchedEffect(Unit) {
userViewModel.getUser()
}
但这也会在配置更改的情况下重新调用,例如屏幕旋转。为防止这种情况,您有两种选择:
- 在视图模型中调用
getUser
init
:在这种情况下,保证只调用一次。
- 在视图模型中创建一些标志以防止冗余请求。
有关 documentation 中 Compose 副作用的更多信息。
我也遇到过这样的问题。解决方案变成了在组合中使用 LauchedEffect 导航。
之前:
if (hasFlight) {
navController.navigate(Screen.StartMovingScreen.route)
}
之后:
if (hasFlight) {
LaunchedEffect(Unit) {
navController.navigate(Screen.StartMovingScreen.route)
}
}
我正在使用 Jetpack Compose 从共享首选项迁移到数据存储。一切正常(数据已保存并可以成功撤回)。但是,无论何时检索数据,可组合项都会不断地重新组合。我正在使用 MVVM 架构,下面是我如何实现数据存储。
下面是在我的AppModule.kt
中声明的SingletonComponent中的App模块
@Provides
@Singleton
fun provideUserPreferenceRepository(@ApplicationContext context: Context):
UserPreferencesRepository = UserPreferencesRepositoryImpl(context)
然后这是我的 ViewModel:
@HiltViewModel
class StoredUserViewModel @Inject constructor(
private val _getUserDataUseCase: GetUserDataUseCase
): ViewModel() {
private val _state = mutableStateOf(UserState())
val state: State<UserState> = _state
fun getUser(){
_getUserDataUseCase().onEach { result ->
val name = result.name
val token = result.api_token
_state.value = UserState(user = UserPreferences(name, agentCode, token, balance))
}.launchIn(viewModelScope)
}}
最后,这是我的存储库实现:
class UserPreferencesRepositoryImpl @Inject constructor(
private val context: Context
): UserPreferencesRepository {
private val Context.dataStore by preferencesDataStore(name = "user_preferences")
}
private object Keys {
val fullName = stringPreferencesKey("full_name")
val api_token = stringPreferencesKey("api_token")
}
private inline val Preferences.fullName get() = this[Keys.fullName] ?: ""
private inline val Preferences.apiToken get() = this[Keys.api_token] ?: ""
override val userPreferences: Flow<UserPreferences> = context.dataStore.data.catch{
// throws an IOException when an error is encountered when reading data
if (it is IOException) {
emit(emptyPreferences())
} else {
throw it
}
}.map { preferences ->
UserPreferences(name = preferences.fullName, api_token = preferences.apiToken)
}.distinctUntilChanged()
我不知道是什么导致可组合项重组。下面是可组合项:
@Composable
fun LoginScreen(
navController: NavController,
userViewModel: StoredUserViewModel = hiltViewModel()
) {
Log.v("LOGIN_SCREEN", "CALLED!")
userViewModel.getUser()
}
如果有人能告诉我哪里做错了请赐教。我尝试更改 AppModule 中 UserPreferencesRepository 的实现,但没有成功。
下面是UseState.kt这只是一个数据class
data class UserState(
val user: UserPreferences? = null
)
下面是UserPreferences.kt
data class UserPreferences(val name: String, val api_token: String)
这是预期的行为:您在每次重组时调用 getUser
。
@Composable
函数是一个视图构建器,应该没有副作用。
相反,您可以使用特殊的副作用函数,例如 LaunchedEffect
,它只会启动一次作业,直到它从视图树中删除或 key
参数被更改:
LaunchedEffect(Unit) {
userViewModel.getUser()
}
但这也会在配置更改的情况下重新调用,例如屏幕旋转。为防止这种情况,您有两种选择:
- 在视图模型中调用
getUser
init
:在这种情况下,保证只调用一次。 - 在视图模型中创建一些标志以防止冗余请求。
有关 documentation 中 Compose 副作用的更多信息。
我也遇到过这样的问题。解决方案变成了在组合中使用 LauchedEffect 导航。
之前:
if (hasFlight) {
navController.navigate(Screen.StartMovingScreen.route)
}
之后:
if (hasFlight) {
LaunchedEffect(Unit) {
navController.navigate(Screen.StartMovingScreen.route)
}
}