簡體   English   中英

StateFlow 收集發出 NullPointerException

[英]StateFlow collect emit NullPointerException

我的存儲庫層有一個MutableStateFlow ,將它收集在我的 ViewModel 中。 我在一些用戶設備上得到這個 NPE

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是非空數據,如果數據以某種方式是 null,則應用程序會更早崩潰。

我如何在存儲庫(生產者)層上使用StateFlow的示例:

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
}

ViewModel(消費者)方面:

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

來自StateFlow 文檔

State 流程永遠不會完成。 在 state 流上對 Flow.collect 的調用永遠不會正常完成,由 Flow.launchIn function 啟動的協程也不會正常完成。

並且Flow 文檔建議捕獲這樣的異常

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

那么是建議吞下collect StateFlow中的所有異常,還是只吞下生產者端拋出的異常? NPE的一般原因是什么?

“如果我們只是調用取消,並不意味着協程工作就會停止。”

我使用ensureActive()檢查協程是否處於活動狀態,解決了這個問題

...
       job = uiScope.launch {
                ensureActive()
                repository.dataFlow().collect { // crash sometimes here.
                    // consume values
                }
        }

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

您可以在How to cancel collect coroutine StateFlow? 中查看此解決方案的詳細信息?

我遇到了同樣的問題。 實際上,我正在將 StateFlow 映射到其他東西並收集它。 在我使用的映射過程中!! 符號。 刪除該符號解決了我的問題。

我認為這可以解決您的問題

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

我遇到了同樣的問題,但是在重新創建片段時發生了這種情況,因為Stateflow仍然有一個舊值,所以只需在調用flow.collect之前添加一個檢查,檢查流是否包含類似這樣的初始化值

if (viewModel.mutableStateFlow.value == YourInitValue) {
        lifecycleScope.launch {
            viewModel.mutableStateFlow.timeLineData.collect {
        
            }
        }
    }

因為如果 stateflow 不包含 init 值,則表示之前調用了 collect 方法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM