Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
525 views
in Technique[技术] by (71.8m points)

android - StateFlow collect emit NullPointerException

My repository layer have a MutableStateFlow, collecting it in my ViewModel. I am getting this NPE on some user devices

Fatal Exception: java.lang.NullPointerException
       at a.b.c.ui.viewmodel.HomeViewModel$collectFlowState$$inlined$collect$1.emit(HomeViewModel.java:189)
       at a.b.c.ui.viewmodel.HomeViewModel$collectFlowState$$inlined$collect$1$1.invokeSuspend(HomeViewModel.java:12)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(BaseContinuationImpl.java:33)
       at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTaskKt.java:176)
       at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTaskKt.java:111)
       at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.java:308)
       at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.java:318)
       at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.java:400)
       at kotlinx.coroutines.android.HandlerContext$scheduleResumeAfterDelay$$inlined$Runnable$1.run(HandlerContext.java:19)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:237)
       at android.app.ActivityThread.main(ActivityThread.java:7830)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1040)

MutableStateFlow is of non-null data, if the data is somehow null the app would have crashed earlier.

An example how I am using StateFlow on the repository (producer) layer:

data class ApiData(...)
private val INITIAL = ApiData(...)
private var someState = INITIAL

private val dataSF = MutableStateFlow(someState)

fun dataFlow() = dataSF

// called on remote api success, we poll for updated data (delta) from the server
fun onDataChangeAvailable(x: Int, y: Double) {
        someState = someState.copy(x = x, y= y)
        dataSF.value = someState
}

The ViewModel (consumer) side:

private val repository // constructor injected; repository is Application scoped
private val job = SupervisorJob()
private val uiScope = CoroutineScope(Dispatchers.Main + job)
// Viewmodel init block
init {
     uiScope.launch {
                repository.dataFlow().collect { // crash sometimes here.
                    // consume values
                }
        }
}

override fun onCleared() {
        job.cancel()
        super.onCleared()
    }

From the StateFlow doc

State flow never completes. A call to Flow.collect on a state flow never completes normally, and neither does a coroutine started by the Flow.launchIn function.

and the Flow doc recommends to catch exceptions like this

try {
    flow.collect { value ->
        println("Received $value")
    }
} catch (e: Exception) {
    println("The flow has thrown an exception: $e")
}

So is it recommended to swallow all exceptions from the collect of a StateFlow or only those thrown by the producer end? Can anybody help me understanding what is the cause of the NPE in general?

Thanks


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I think this could solve your problem

private val dataSF = MutableStateFlow<Int?>(someState)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...