【问题标题】:Jetpack compose data store keeps recomposing screenJetpack 撰写数据存储不断重组屏幕
【发布时间】:2022-01-18 12:31:33
【问题描述】:

我正在使用 jetpack compose 从共享偏好迁移到数据存储。一切正常(数据已保存并可成功撤回)。但是,每当检索到数据时,可组合项就会不断地重新组合。我正在使用 MVVM 架构,下面是我实现数据存储的方式。

下面是在我的 AppModule.kt 中声明的

SingletonComponent 中的应用模块

@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
        val balance = result.balance
        val agentCode = result.agent_code
        _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")
    val balance = intPreferencesKey("balance")
    val agentCode = intPreferencesKey("agent_code")
}

private inline val Preferences.fullName get() = this[Keys.fullName] ?: ""
private inline val Preferences.apiToken get() = this[Keys.api_token] ?: ""
private inline val Preferences.balance get() = this[Keys.balance] ?: 0
private inline val Preferences.agentCode get() = this[Keys.agentCode] ?: 0

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, agent_code = preferences.agentCode, api_token = preferences.apiToken,
        balance = preferences.balance)
}.distinctUntilChanged()

我不知道是什么导致可组合物重新组合。下面是可组合的:

@Composable
fun LoginScreen(
navController: NavController,
userViewModel: StoredUserViewModel = hiltViewModel()
) {

Log.v("LOGIN_SCREEN", "CALLED!")
userViewModel.getUser()
}

如果有人能告诉我哪里做错了,请赐教。我试图在 AppModule 中更改 UserPreferencesRepository 的实现,但没有成功。

下面是 UseState.kt,它只是一个数据类

data class UserState(
val user: UserPreferences? = null
)

下面是 UserPreferences.kt

data class UserPreferences(val name: String, val agent_code: Int, val api_token: String, val balance: Int)

【问题讨论】:

  • 你从来没有发过UserState所以我们不知道
  • @EpicPandaForce 我已经为 UserState 添加了代码,这只是一个数据类
  • 好的,现在我们需要UserPreferences :D
  • 它也是一个数据类,我已经编辑了问题并包含了它。 @EpicPandaForce
  • 我猜,也许使用stateIn 来处理您的流程

标签: android dagger-2 android-jetpack-compose android-jetpack dagger-hilt


【解决方案1】:

这是预期的行为:您在每次重组时都调用getUser

@Composable 函数是一个视图构建器,应该没有副作用。

您可以使用特殊的副作用函数,例如 LaunchedEffect,它只会启动一次作业,直到它从视图树中删除或 key 参数被更改:

LaunchedEffect(Unit) {
    userViewModel.getUser()
}

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

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

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

【讨论】:

  • ?,我觉得有一件小事我没有提到。仅当我导航到另一个可组合项时,才会调用可组合项。但最初我可以调用 getUser 并且它根本不会在循环中重组。但是,一旦我导航到另一个可组合对象,即使我在导航之前弹出堆栈以将其从堆栈中删除,它也会继续循环重组。最初我在 viewModel init 中调用 getUser 但这并没有解决问题。也许旗帜的想法必须有所帮助。让我挂个旗子看看。谢谢老哥
  • @timtimer 在导航转换期间多次组合导航重新组合视图,这是预期的行为。在这种情况下,应该使用LaunchedEffect 来防止多次调用。
  • 如果你不需要协程,你也可以使用DisposableEffect(Unit) {和一个空的onDispose {
  • @PhilipDukhov LaunchedEffect 不起作用。我得到了同样的结果。
猜你喜欢
  • 2022-11-12
  • 2021-07-12
  • 1970-01-01
  • 2023-01-25
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多