防止 LaunchedEffect 在配置更改时重新运行

问题描述 投票:0回答:4

我只想在加载可组合项时运行代码一次。因此,我使用 LaunchedEffect 并将 key 设置为 true 来实现此目的。

LaunchedEffect(true) {
    // do API call
}

此代码工作正常,但每当有任何配置更改(例如屏幕旋转)时,此代码都会再次执行。如果配置更改,如何防止它再次运行?

android kotlin android-jetpack-compose android-jetpack
4个回答
16
投票

最简单的解决方案是存储有关您是否使用

rememberSaveable
进行 API 调用的信息:它会在配置更改时生效。

var initialApiCalled by rememberSaveable { mutableStateOf(false) }
if (!initialApiCalled) {
    LaunchedEffect(Unit) {
        // do API call
        initialApiCalled = false
    }
}

此解决方案的缺点是,如果在 API 调用完成之前配置发生更改,

LaunchedEffect
协程将被取消,您的 API 调用也将被取消。

最干净的解决方案是使用视图模型,并在内部执行API调用

init

class ScreenViewModel: ViewModel() {
    init {
        viewModelScope.launch {
            // do API call
        }
    }
}

@Composable
fun Screen(viewModel: ScreenViewModel = viewModel()) {
    
}

官方文档推荐像这样传递视图模型作为参数。在产品代码中,您不需要向此视图传递任何参数,只需像 Screen()

 那样调用它即可:视图模型将由默认的 
viewModel()
 参数创建。它已移至测试/预览功能的参数,如
此答案所示。


1
投票
我认为最好的方法是在 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. } }
使用此代码时,您根本不必在活动中调用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 } } } } } }
}}


0
投票
我尝试过

LaunchedEffect(lifeCycleOwner) { }
看起来不错


-1
投票
@

Islam Mansour 答案对于 UI 的专用 viewModel 很有用,但我的情况是由许多 UI 片段共享 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>() }

mutableStateListOfLIST_TYPE<

>
当数据添加到此时自动重构UI

by lazy

 附加的变量仅在显式调用时初始化一次

© www.soinside.com 2019 - 2024. All rights reserved.