Home > Software engineering >  Can I use one Job instead of two Jobs when I use Flow in Kotlin?
Can I use one Job instead of two Jobs when I use Flow in Kotlin?

Time:06-10

In Code A, there are two Flows, and I assign two jobs for them, I collect the two Flows in fun beginSoundDensity() and stop collecting the two Flows in fun resetSoundDensity().

I think there are many repeated codes in Code A, so I hope to improve it, but Code B doesn't work.

Can I use one Job in my case?

Code A

private val _soundDensityState = MutableStateFlow(initialMSoundDensity)
val soundDensityState = _soundDensityState.asStateFlow()

private val _timeX = MutableStateFlow(0)
val timeX = _timeX.asStateFlow()

private var myJob1: Job?=null
private var myJob2: Job?=null

val myFlow: Flow<Int> = flow {
    var i = 0
    while (true) {
        emit(i)
        i = i   15
        delay(5000)
    }
}

fun beginSoundDensity() {
        myJob1?.cancel()
        myJob2?.cancel()

        myJob1 = viewModelScope.launch {
            aSoundMeter.startSoundDensity {
                pauseSoundDensity()  
            }.cancellable()
                .collect {
                    _soundDensityState.value = it
                }
        }

        myJob2 = viewModelScope.launch {
            myFlow.collect {
                _timeX.value = it
            }
        }

    }
}

fun resetSoundDensity(){
    myJob1?.cancel()
    myJob2?.cancel()
}

Code B

//The same

private var myJob: Job?=null       

val myFlow: Flow<Int> = flow {
    var i = 0
    while (true) {
        emit(i)
        i = i   15
        delay(5000)
    }
}

fun beginSoundDensity() {
        myJob?.cancel()
        
        myJob = viewModelScope.launch {
            aSoundMeter.startSoundDensity {
                pauseSoundDensity()  
            }.cancellable()
                .collect {
                    _soundDensityState.value = it
                }

            myFlow.collect {
                _timeX.value = it      //It will not be launched
            }
        }
      
    }
}

fun resetSoundDensity(){
    myJob?.cancel()       
}
 

CodePudding user response:

Yes and no. You need two separate coroutines running concurrently to collect from two flows. In your Code B myFlow will be collected only after aSoundMeter finishes collecting. Collections need to run at the same time, so you need two concurrent coroutines for this purpose.

However, if you always start and cancel both collections together, then I think it would be better to group them into a single coroutine like this:

fun beginSoundDensity() {
    myJob?.cancel()
        
    myJob = viewModelScope.launch {
        coroutineScope {
            launch {
                aSoundMeter.startSoundDensity {
                    pauseSoundDensity()  
                }.cancellable()
                    .collect {
                        _soundDensityState.value = it
                    }
            }
            launch {
                myFlow.collect {
                    _timeX.value = it      //It will not be launched
                }
            }
        }
    }
}

fun resetSoundDensity(){
    myJob?.cancel()       
}
  • Related