java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object kotlinx.coroutines.flow.FlowCollector.emit
Asked Answered
E

1

10

Crash as soon as flow emits its first value.

java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object kotlinx.coroutines.flow.FlowCollector.emit(java.lang.Object, kotlin.coroutines.Continuation)' on a null object reference
    at kotlinx.coroutines.flow.FlowKt__ErrorsKt$catchImpl$$inlined$collect$1.emit(Collect.kt:136)
    at com.happyfleet.app.data.UsbDataSource$mapResponse$$inlined$map$1$2.emit(Collect.kt:137)
    at com.happyfleet.app.data.UsbDataSource$mapResponse$$inlined$filter$1$2.emit(Collect.kt:137)
    at kotlinx.coroutines.flow.SharedFlowImpl.collect(SharedFlow.kt:351)
    at kotlinx.coroutines.flow.SharedFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:234)
    at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:190)
    at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:161)
    at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
    at kotlinx.coroutines.flow.SharedFlowImpl.emitSuspend(SharedFlow.kt:472)
    at kotlinx.coroutines.flow.SharedFlowImpl.emit(SharedFlow.kt:374)

My ViewModel code:

@HiltViewModel
class ErrorActivityViewModel @Inject constructor(
    private val sendActivityReportUseCase: SendActivityReportUseCase,
    private val connectionRepository: ConnectionRepository
) : ViewModel() {

    fun sendActivityReport() = viewModelScope.launch {
        sendActivityReportUseCase.execute()
    }


    init {
        subscribeToSessionTime()
    }

    private fun subscribeToSessionTime() = viewModelScope.launch {
        val sessionTimeout = connectionRepository.getSessionTimeoutFlow()
        _sessionTimeoutFlow.emitAll(sessionTimeout)
    }

    private val _sessionTimeoutFlow = MutableSharedFlow<Int>()
    val sessionTimeoutFlow = _sessionTimeoutFlow.asSharedFlow()


    override fun onCleared() {
        super.onCleared()
        sendActivityReportUseCase.cancel()
    }
}
Endplay answered 6/4, 2022 at 7:56 Comment(1)
related #57128945Lora
E
38

The reason

sessionTimeout flow is connected to the _sessionTimeoutFlow with emitAll operator before _sessionTimeoutFlow value is assigned. Because compiler goes from top to the bottom. As a result, first init is called while _sessionTimeoutFlow is null, then _sessionTimeoutFlow gets its value in the line 32.

Solution

The order is important here. Move flow declaration BEFORE init as it's shown on the image.

enter image description here

Endplay answered 6/4, 2022 at 7:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.