Search code examples
androidnullpointerexceptioncoroutine

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


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()
    }
}

Solution

  • 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