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()
}

但这也会在配置更改的情况下重新调用,例如屏幕旋转。为防止这种情况,您有两种选择:

  1. 在视图模型中调用 getUser init:在这种情况下,保证只调用一次。
  2. 在视图模型中创建一些标志以防止冗余请求。

有关 documentation 中 Compose 副作用的更多信息。

我也遇到过这样的问题。解决方案变成了在组合中使用 LauchedEffect 导航。

之前:

if (hasFlight) {
    navController.navigate(Screen.StartMovingScreen.route)
}

之后:

if (hasFlight) {
    LaunchedEffect(Unit) {
        navController.navigate(Screen.StartMovingScreen.route)
    }
}