Alexis Delahaye
10/16/2024, 11:33 AMAlexis Delahaye
10/16/2024, 11:41 AMfun LoginScreen(
loginViewModel: LoginViewModel = hiltViewModel(),
) {
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(key1 = lifecycleOwner) {
loginViewModel.performIntent(ScanUiIntent.StartScan)
}
}
fun ScanMachineScreen(
scanMachineViewModel: ScanMachineViewModel = hiltViewModel(),
) {
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(key1 = lifecycleOwner) {
scanMachineViewModel.performIntent(ScanUiIntent.StartScan)
}
}
Both LoginVM and ScanMachineVM inherit of ScanVM, where I collect a scanData flow
@JvmInline
value class Scan(
val data: String,
)
data class ScanUiState(
val scanData: Scan? = null,
)
sealed class ScanUiIntent {
data object StartScan : ScanUiIntent()
data object StopScan : ScanUiIntent()
}
@HiltViewModel
open class ScanViewModel
@Inject
constructor(
private val scanRepository: ScanRepository,
scanDataSource: ScanDataSource,
) : BaseViewModel() {
var scanUiState by mutableStateOf(ScanUiState())
private set
private var scanJob: Job? = null
private val scanData: SharedFlow<Scan?> =
scanDataSource
.scanDataFlow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = null,
)
fun performIntent(intent: ScanUiIntent) {
when (intent) {
is ScanUiIntent.StartScan -> startScan()
is ScanUiIntent.StopScan -> stopScan()
}
}
init {
// Collect the hot flow and update the state
// startScan()
}
private fun startScan() {
Log.d("ScanViewModel", "startScan() scanJob = $scanJob")
if (scanJob != null) return // Prevent multiple collections
scanJob =
viewModelScope.launch {
scanData
.filter { scannedData -> !scannedData?.data.isNullOrEmpty() }
.collectLatest { scanData ->
scanUiState = scanUiState.copy(scanData = scanData)
Log.d("ScanViewModel", "scanUiState.scanData set to :: ${scanUiState.scanData}")
}
}
}
// Stop collecting scan data
private fun stopScan() {
scanJob?.cancel()
scanJob = null
}
}
The problem I face is that, on each navigation event, the startScan() method seems to be triggered once again (the amount of logs of the method increment)
This behavior occurs whether I use the startScan method in the ScanVM init block or in each screen
class ScanDataSource
@Inject
constructor(
@ApplicationContext private val context: Context,
) {
private val _scanDataFlow = MutableSharedFlow<Scan?>(replay = 1)
val scanDataFlow: SharedFlow<Scan?> = _scanDataFlow.asSharedFlow()
init {
val scanReceiver =
object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent,
) {
if (intent.action != Datawedge.SCAN_ACTION) return
val scanData = intent.getStringExtra(Datawedge.STRING_EXTRA)
val emitted = _scanDataFlow.tryEmit(if (scanData.isNullOrBlank()) null else Scan(scanData))
Log.d("ScanDataSource", "scan data emitted ? $emitted -> $scanData")
}
}
val intentFilter =
IntentFilter().also {
it.addAction(Datawedge.SCAN_ACTION)
it.addCategory(Datawedge.SCAN_CATEGORY)
}
context.registerReceiver(scanReceiver, intentFilter)
Log.d("ScanDataSource", "getScanData: register receiver")
}
}
The ScanDataSource works correctly and only emit once, the problem seems to come from the way I collect it in the VM
Thank you for your help
Nota : I think I messed up with startScan collecting a cold flow and scanData being a hot flow김용석
10/17/2024, 6:54 AM