使用Compose withKotlin时,从Flow收集的操作是否会占用大量系统资源?

cgh8pdjw  于 2023-01-31  发布在  Kotlin
关注(0)|答案(2)|浏览(170)

SoundViewModelViewModel类,val listSoundRecordState可能会被App中的一些模块使用。
在代码A中,我在需要使用数据listSoundRecordState时调用了fun collectListSoundRecord(),但是fun collectListSoundRecord()可能会因为Jetpack Compose重组而反复启动,不知道会不会耗费很多系统资源?
在代码B中,我在init { }中启动private fun collectListSoundRecord()collectListSoundRecord()只会启动一次,但即使我不需要使用数据listSoundRecordState,它也会一直保存在内存中,直到App代码关闭,这种方式会消耗很多系统资源吗?

    • 代码A**
@HiltViewModel
class SoundViewModel @Inject constructor(
  ...
): ViewModel() {

    private val _listSoundRecordState = MutableStateFlow<Result<List<MRecord>>>(Result.Loading)
    val listSoundRecordState = _listSoundRecordState.asStateFlow()

    init { }

     //It may be launched again and again
    fun collectListSoundRecord(){
        viewModelScope.launch {
            listRecord().collect {
                result -> _listSoundRecordState.value =result
            }
        }
    }

    private fun listRecord(): Flow<Result<List<MRecord>>> {
        return  aSoundMeter.listRecord()
    }

}
    • 代码B**
@HiltViewModel
class SoundViewModel @Inject constructor(
  ...
): ViewModel() {

    private val _listSoundRecordState = MutableStateFlow<Result<List<MRecord>>>(Result.Loading)
    val listSoundRecordState = _listSoundRecordState.asStateFlow()

    init { collectListSoundRecord() }

    private fun collectListSoundRecord(){
        viewModelScope.launch {
            listRecord().collect {
                result -> _listSoundRecordState.value =result
            }
        }
    }

    private fun listRecord(): Flow<Result<List<MRecord>>> {
        return  aSoundMeter.listRecord()
    }

}
llycmphe

llycmphe1#

只有当中间流(保存在SoundViewModel中的流)有订阅者时,才收集原始流(来自listRecord())并缓存结果,这可能会使您受益。
在您的例子中,订阅者是一个Composable函数,它收集值(并且可能经常重新组合)。
可以使用stateIn()的非挂起变体来实现这一点,因为您有一个默认值。

@HiltViewModel
class SoundViewModel @Inject constructor(
    ...
): ViewModel() {

    val listSoundRecordState = listRecord().stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Result.Loading)

    private fun listRecord(): Flow<Result<List<MRecord>>> {
        return  aSoundMeter.listRecord()
    }
}

为了从UI层使用StateFlow@Composable函数),您必须将其转换为State,如下所示:

val viewModel: SoundViewModel = viewModel()
val listSoundRecord = viewModel.listSoundRecordState.collectAsState()
qv7cva1a

qv7cva1a2#

我使用下面的composable实现,其中view-model使用LaunchedEffect

宠物小精灵详细屏幕.kt

@Composable
fun PokemonDetailScreen(
    dominantColor: Color,
    pokemonName: String,
    navController: NavController,
    topPadding: Dp = 20.dp,
    pokemonImageSize: Dp = 200.dp,
    viewModel: PokemonDetailVm = hiltViewModel()
) {

    var pokemonDetailData by remember { mutableStateOf<PokemonDetailView>(PokemonDetailView.DisplayLoadingView) }
    val pokemonDetailScope = rememberCoroutineScope()

    LaunchedEffect(key1 = true){
        viewModel.getPokemonDetails(pokemonName)
        viewModel.state.collect{ pokemonDetailData = it }
    }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(dominantColor)
            .padding(bottom = 16.dp)
    ) {

        PokemonHeader(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(0.2f)
                .align(Alignment.TopCenter)
        ) {
            navController.popBackStack()
        }

        PokemonBody(
            pokemonInfo = pokemonDetailData,
            topPadding = topPadding,
            pokemonImageSize = pokemonImageSize
        ) {
            pokemonDetailScope.launch {
                viewModel.getPokemonDetails(pokemonName)
            }
        }

        Box(
            contentAlignment = Alignment.TopCenter,
            modifier = Modifier
                .fillMaxSize()
        ) {

            if(pokemonDetailData is PokemonDetailView.DisplayPokemonView){

                val data = (pokemonDetailData as PokemonDetailView.DisplayPokemonView).data

                data.sprites.let {
                    // Image is available
                    val url = PokemonUtils.formatPokemonDetailUrl(it.frontDefault)
                    AsyncImage(
                        model = ImageRequest.Builder(LocalContext.current)
                            .data(url)
                            .crossfade(true)
                            .build(),
                        contentDescription = data.name,
                        contentScale = ContentScale.Fit,
                        modifier = Modifier
                            // Set the default size passed to the composable
                            .size(pokemonImageSize)
                            // Shift the image down from the top
                            .offset(y = topPadding)
                    )
                }
            }
        }
    }

}

宠物小精灵详细信息虚拟机kt

@HiltViewModel
class PokemonDetailVm @Inject constructor(
    private val repository: PokemonRepositoryFeature
): ViewModel(){

    private val _state = MutableSharedFlow<PokemonDetailView>()
    val state = _state.asSharedFlow()

    suspend fun getPokemonDetails(pokemonName:String) {
        viewModelScope.launch {
            when(val pokemonInfo = repository.getPokemonInfo(pokemonName)){
                is Resource.Error -> {
                    pokemonInfo.message?.let {
                        _state.emit(PokemonDetailView.DisplayErrorView(message = it))
                    }
                }
                is Resource.Loading -> {
                    _state.emit(PokemonDetailView.DisplayLoadingView)
                }
                is Resource.Success ->  {
                    pokemonInfo.data?.let {
                        _state.emit(PokemonDetailView.DisplayPokemonView(data = it))
                    }
                }
            }
        }
    }

}

相关问题