如何更新我的可组合服务属性更新



我正在使用服务定位器(如https://developer.android.com/training/dependency-injection#di-备选方案,但我保证稍后会切换到适当的DI)来处理我的应用程序中的身份验证。我有一个身份验证服务,它具有user属性,我使用logInlogOut方法设置和取消设置该属性

我希望我的ContentView能对auth.user的变化做出反应,但我不太清楚如何。我已经尝试将其包装到by remember { mutableStateOf() }中,但登录时没有看到任何更新。。知道我缺了什么吗?

提前感谢!(以下片段)


@Composable
fn ContentView() {
val auth = ServiceLocator.auth
var loggedInUser: User? by remember { mutableStateOf(auth.user) }  // <-- I would like my composable to react to changes to auth.user
if (loggedInUser) {
ViewA()
} else {
ViewB()
}
}
object ServiceLocator {
val auth = AuthenticationService()
}
class AuthenticationService {
var user: User? = null
fun logIn() { 
// sets user...
}
fun logOut() {
// undefs user...
}

在这行的代码片段中

var loggedInUser: User? by remember { mutableStateOf(auth.user) }

您正在创建一个MutableState<User?>的实例,该实例具有auth.user当时引用的值的初始值。由于remember { },只有当可组合的ContentView进入组合,然后MutableState实例在重新组合中被记住并重用时,才会发生这种初始化。

如果以后更改变量auth.user,则不会发生重新组合,因为存储在loggedInUser中的值(处于可变状态)没有更改。

mutableStateOf的文档解释了这个调用在幕后的实际作用

返回一个用传入值初始化的新MutableState

MutableState类是一个单独的值持有者,Compose会观察其读写操作。此外,对它的写入作为Snapshot系统的一部分进行事务处理。

让我们仔细分析一下这条信息。

返回一个用传入值初始化的新MutableState。

调用mutableStateOf返回一个MutableState实例,该实例使用作为参数传递的值进行初始化。

MutableState类是一个单一值持有者

此类的每个实例都存储一个state值。它可能存储用于实现目的的其他值,但它只公开单个状态值。

其读写由Compose 观察

Compose观察发生在MutableState实例上的读写操作

这是您遗漏的信息。写入需要发生在MutableState的实例上(在您的情况下为loggedInUser),而不是发生在作为初始值传入的变量上(在我们的情况下是auth.user)。

如果你真的想一想,Kotlin中没有内置的机制来观察变量的变化,所以Compose必须有一个包装器才能观察变化是可以理解的。我们必须通过包装器来更改状态,而不是直接更改变量。


知道所有这些,只需将可变状态移动到AuthenticationService中,事情就会在中工作

import androidx.compose.runtime.mutableStateOf
class AuthenticationService {
var user: User? by mutableStateOf(null)
private set

// ... rest of the service
}
@Composable
fun ContentView() {
val auth = ServiceLocator.auth
// no remember { } block this time because now the MutableState reference is being kept by
// the AuthenticationService so it won't reset on recomposition
val loggedInUser = auth.user
if (loggedInUser != null) {
ViewA()
} else {
ViewB()
}
}

但是,现在您的AuthenticationService依赖于mutableStateOf,因此依赖于您可能想要避免的可组合运行时。A";"服务";(或Repository)不需要知道有关UI实现的详细信息。

还有其他选项可以跟踪状态更改,而不依赖于Compose运行时。来自文档部分Compose和其他库

Compose为Android最流行的基于流的应用程序提供了扩展解决方案。这些扩展中的每一个都由不同的工件:

  • Flow.collectAsState()不需要额外的依赖项。(因为它是kotlinx-coroutines-core的一部分)

  • 包括在CCD_ 29伪影中的CCD_。

  • androidx.compose.runtime:runtime-rxjava2:$composeVersion中包含的Observable.subscribeAsState()或CCD_ 32伪影。

这些工件注册为侦听器,并将值表示为State。每当发出新值时,Compose都会重新组合这些零件其中CCD_ 34被使用。

使用KotlinMutableStateFlow的示例

// No androidx.compose.* dependencies anymore
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class AuthenticationService {
private val user = MutableStateFlow<User?>(null)
val userFlow = user.asStateFlow()
fun logIn() {
user.value = User(/* potential parameters */)
}
fun logOut() {
user.value = null
}
}

然后在可组合中,我们收集流作为状态。

import androidx.compose.runtime.collectAsState
@Composable
fun ContentView() {
val auth = ServiceLocator.auth
val loggedInUser = auth.userFlow.collectAsState().value
if (loggedInUser != null) {
ViewA()
} else {
ViewB()
}
}

要了解有关在Compose中使用状态的更多信息,请参阅管理状态的文档部分。这是能够有效地处理Compose中的状态并触发重新计算的基本信息。它还涵盖了状态提升的基本原理。如果你更喜欢编码教程,这里是Jetpack Compose中State的代码实验室。

最终(当你的应用程序变得越来越复杂时),你可能想在你的服务/存储库层和UI层(可组合)之间再加一层。一个层,它将保存和管理UI状态,以便您能够涵盖积极的结果(成功登录)和消极的结果(失败登录)。

如果采用MVVM(Model View ViewModel)方式或MVI(Model View Intent)方式,则ViewModels将覆盖该层。在这种情况下,可组合程序本身只管理一些瞬态UI状态,而它们从VM获取(或观察)其余的UI状态,并调用VM执行操作。然后,虚拟机与服务/存储库层交互,并相应地更新UI状态。在谷歌关于使用Jetpack Compose的自动状态观察的视频中,介绍了如何随着复杂性的增加来处理状态。

最新更新