防止 LaunchedEffect 在配置更改时重新 运行
Prevent LaunchedEffect from re-running on configuration change
我只想在加载可组合项时 运行 代码一次。所以我使用 LaunchedEffect with key as true 来实现这个。
LaunchedEffect(true) {
// do API call
}
此代码运行良好,但每当有任何配置更改(如屏幕旋转)时,都会再次执行此代码。如果配置更改,如何防止它再次 运行ning?
最简单的解决方案是存储有关您是否使用 rememberSaveable
进行 API 调用的信息:它会在配置更改时生效。
var initialApiCalled by rememberSaveable { mutableStateOf(false) }
if (!initialApiCalled) {
LaunchedEffect(Unit) {
// do API call
initialApiCalled = false
}
}
此解决方案的缺点是,如果在 API 调用完成之前更改配置,LaunchedEffect
协程将被取消,您的 API 调用也会被取消。
最干净的解决方案是使用视图模型,并在 init
:
中执行 API 调用
class ScreenViewModel: ViewModel() {
init {
viewModelScope.launch {
// do API call
}
}
}
@Composable
fun Screen(viewModel: ScreenViewModel = viewModel()) {
}
official documentation. In the prod code you don't need to pass any parameter to this view, just call it like Screen()
: the view model will be created by default viewModel()
parameter. It is moved to the parameter for test/preview capability as shown in 推荐像这样传递视图模型作为参数。
我假设最好的方法是在 livedata/stateflow 惰性创建上使用 .also ,这样您就可以保证只要视图模型处于活动状态,loadState 只会被调用一次,并且还可以保证除非有人正在收听,否则不会调用服务本身。然后你从视图模型中监听状态,并且不需要调用任何东西 api 从启动的效果中调用,你的代码也会对特定状态做出反应。
这是一个代码示例
class MyViewModel : ViewModel() {
private val uiScreenState: : MutableStateFlow<WhatEverState> =
MutableStateFlow(WhatEverIntialState).also {
loadState()
}
fun loadState(): StateFlow<WhatEverState>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
使用这段代码的时候,根本不需要在activity中调用loadstate,你只需要听观察者。
您可以查看下面的收听代码
class MyFragment : Fragment {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
StartingComposeTheme {
Box(modifier = Modifier.fillMaxSize()) {
val state by viewModel.uiScreenState.collectAsState()
when (state) {
//do something
}
}
}
}
}
}
}}
@Islam Mansour 对 UI 的专用 viewModel 的回答很有效,但我的案例由许多 UIs 片段共享 ViewModel
在我的例子中,上面的答案并没有解决我的问题,因为当用户导航到相关的 UI 部分时,只是第一次调用 API。
因为 我在 NavHost
中有多个可组合的 UI 作为 Fragment
而我的ViewModel
遍历所有片段
因此,API 应该只在用户导航到所需片段时调用
所以,下面的惰性 属性 初始化程序解决了我的问题;
val myDataList by lazy {
Log.d("test","call only once when called from UI used inside)")
loadDatatoThisList()
mutableStateListOf<MyModel>()
}
mutableStateListOf<LIST_TYPE
> 当数据添加到此
时自动重组UI
由 by lazy
应用的变量仅在显式调用时初始化一次
我只想在加载可组合项时 运行 代码一次。所以我使用 LaunchedEffect with key as true 来实现这个。
LaunchedEffect(true) {
// do API call
}
此代码运行良好,但每当有任何配置更改(如屏幕旋转)时,都会再次执行此代码。如果配置更改,如何防止它再次 运行ning?
最简单的解决方案是存储有关您是否使用 rememberSaveable
进行 API 调用的信息:它会在配置更改时生效。
var initialApiCalled by rememberSaveable { mutableStateOf(false) }
if (!initialApiCalled) {
LaunchedEffect(Unit) {
// do API call
initialApiCalled = false
}
}
此解决方案的缺点是,如果在 API 调用完成之前更改配置,LaunchedEffect
协程将被取消,您的 API 调用也会被取消。
最干净的解决方案是使用视图模型,并在 init
:
class ScreenViewModel: ViewModel() {
init {
viewModelScope.launch {
// do API call
}
}
}
@Composable
fun Screen(viewModel: ScreenViewModel = viewModel()) {
}
official documentation. In the prod code you don't need to pass any parameter to this view, just call it like Screen()
: the view model will be created by default viewModel()
parameter. It is moved to the parameter for test/preview capability as shown in
我假设最好的方法是在 livedata/stateflow 惰性创建上使用 .also ,这样您就可以保证只要视图模型处于活动状态,loadState 只会被调用一次,并且还可以保证除非有人正在收听,否则不会调用服务本身。然后你从视图模型中监听状态,并且不需要调用任何东西 api 从启动的效果中调用,你的代码也会对特定状态做出反应。
这是一个代码示例
class MyViewModel : ViewModel() {
private val uiScreenState: : MutableStateFlow<WhatEverState> =
MutableStateFlow(WhatEverIntialState).also {
loadState()
}
fun loadState(): StateFlow<WhatEverState>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
使用这段代码的时候,根本不需要在activity中调用loadstate,你只需要听观察者。
您可以查看下面的收听代码
class MyFragment : Fragment {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
StartingComposeTheme {
Box(modifier = Modifier.fillMaxSize()) {
val state by viewModel.uiScreenState.collectAsState()
when (state) {
//do something
}
}
}
}
}
}
}}
@Islam Mansour 对 UI 的专用 viewModel 的回答很有效,但我的案例由许多 UIs 片段共享 ViewModel
在我的例子中,上面的答案并没有解决我的问题,因为当用户导航到相关的 UI 部分时,只是第一次调用 API。
因为 我在 NavHost
中有多个可组合的 UI 作为 Fragment
而我的ViewModel
遍历所有片段
因此,API 应该只在用户导航到所需片段时调用
所以,下面的惰性 属性 初始化程序解决了我的问题;
val myDataList by lazy {
Log.d("test","call only once when called from UI used inside)")
loadDatatoThisList()
mutableStateListOf<MyModel>()
}
mutableStateListOf<LIST_TYPE
> 当数据添加到此
由 by lazy
应用的变量仅在显式调用时初始化一次