1

I want to run the code only once when the composable is loaded. So I am using LaunchedEffect with key as true to achieve this.

LaunchedEffect(true) {
    // do API call
}

This code is working fine but whenever there is any configuration change like screen rotation this code is executed again. How can I prevent it from running again in case of configuration change?

4

3 回答 3

3

最简单的解决方案是存储有关您是否使用 进行 API 调用的信息rememberSaveable:当配置更改时,它将生效。

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()参数创建。如本答案所示,它已移至测试/预览功能的参数。

于 2021-10-19T11:00:10.463 回答
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
                    }
                }
            }
        }
    }
}

}}

于 2021-10-19T14:12:17.123 回答
0

@Islam Mansour的回答适用于 UI 的专用视图模型,但我的案例是由许多 UI 片段共享的视图模型

在我的情况下,上述答案并不能解决我在用户导航到相关 UI 部分时仅第一次调用 API 的问题。

因为我在 Fragment 中有多个可组合NavHostUI

而我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初始化一次的变量

于 2021-12-05T06:04:54.097 回答