![](/img/trans.png)
[英]Jetpack compose java.lang.IllegalStateException: Invalid applier
[英]java.lang.IllegalStateException when using State in Android Jetpack Compose
我有 Kotlin 密封 class 的ViewModel
为 UI 提供不同的状态。 另外,我使用androidx.compose.runtime.State
object 通知 UI state 的变化。
如果MyApi
请求发生错误,我将UIState.Failure
放入MutableState
object 然后我得到IllegalStateException
:
java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied
at androidx.compose.runtime.snapshots.SnapshotKt.readError(Snapshot.kt:1524)
at androidx.compose.runtime.snapshots.SnapshotKt.current(Snapshot.kt:1764)
at androidx.compose.runtime.SnapshotMutableStateImpl.setValue(SnapshotState.kt:797)
at com.vladuken.compose.ui.category.CategoryListViewModel$1.invokeSuspend(CategoryListViewModel.kt:39)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Viem模型代码:
@HiltViewModel
class CategoryListViewModel @Inject constructor(
private val api: MyApi
) : ViewModel() {
sealed class UIState {
object Loading : UIState()
data class Success(val categoryList: List<Category>) : UIState()
object Error : UIState()
}
val categoryListState: State<UIState>
get() = _categoryListState
private val _categoryListState =
mutableStateOf<UIState>(UIState.Loading)
init {
viewModelScope.launch(Dispatchers.IO) {
try {
val categories = api
.getCategory().schemas
.map { it.toDomain() }
_categoryListState.value = UIState.Success(categories)
} catch (e: Exception) {
//this does not work
_categoryListState.value = UIState.Error
}
}
}
}
我试图延迟设置 UIState.Error - 它起作用了,但我认为这不是正常的解决方案:
viewModelScope.launch(Dispatchers.IO) {
try {
val categories = api
.getCategory().schemas
.map { it.toDomain() }
_categoryListState.value = UIState.Success(categories)
} catch (e: Exception) {
//This works
delay(10)
_categoryListState.value = UIState.Error
}
}
我在 Composable function 中观察到 State object 如下:
@Composable
fun CategoryScreen(
viewModel: CategoryListViewModel,
onCategoryClicked: (Category) -> Unit
) {
when (val uiState = viewModel.categoryListState.value) {
is CategoryListViewModel.UIState.Error -> CategoryError()
is CategoryListViewModel.UIState.Loading -> CategoryLoading()
is CategoryListViewModel.UIState.Success -> CategoryList(
categories = uiState.categoryList,
onCategoryClicked
)
}
}
撰写版本: 1.0.0-beta03
如何使用 Compose State
处理密封的 class UIState
,以免抛出 IllegalStateException?
解决这个问题的三种方法是
在 kotlinlang.slack.com/archives/CJLTWPH7S/p1613581738163700 中有一个关于看起来有点相似的问题的讨论。
我认为该讨论的一些相关部分(来自亚当鲍威尔)
至于快照 state 的线程安全方面,您遇到的是快照是事务性的结果。
当拍摄快照时(合成会在后台为您执行此操作),当前活动的快照是线程本地的。 组合中发生的一切都是该事务的一部分,并且该事务尚未提交。
因此,当您在组合中创建一个新的 mutableStateOf 然后将其传递给另一个线程时,正如问题片段中的 GlobalScope.launch 所做的那样,您实际上已经让对尚不存在的快照 state 的引用从事务中逃脱。
确切的情况在这里有点不同,但我认为同样的关键问题。 可能不会完全这样做,但至少在这里它通过将init
的内容移动到新的getCategories()
方法来工作,然后从LaunchedEffect
块调用该方法。 FWIW 我在其他地方做的这种情况(同时仍在init
中调用)是在视图 model 中使用StateFlow
,然后在 Compose 代码中调用collectAsState()
。
@Composable
fun CategoryScreen(
viewModel: CategoryListViewModel,
onCategoryClicked: (Category) -> Unit
) {
LaunchedEffect(true) {
viewModel.getCategories()
}
when (val uiState = viewModel.categoryListState.value) {
is CategoryListViewModel.UIState.Error -> CategoryError()
is CategoryListViewModel.UIState.Loading -> CategoryLoading()
is CategoryListViewModel.UIState.Success -> CategoryList(
categories = uiState.categoryList,
onCategoryClicked
)
}
}
因此,经过更多尝试解决此问题后,我找到了解决方案。 在https://stackoverflow.com/a/66892156/13101450答案的帮助下,我知道快照是事务性的并且在 ui 线程上运行 - 更改调度程序帮助:
viewModelScope.launch(Dispatchers.IO) {
try {
val categories = api
.getCategory().schemas
.map { it.toDomain() }
_categoryListState.value = UIState.Success(categories)
} catch (e: Exception) {
withContext(Dispatchers.Main) {
_categoryListState.value = UIState.Error
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.