Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CoroutineExceptionHandler doesn't work with ViewModelScope and Koin

I have a method to fetch devices using coroutines and use viewModelScope.launch to run coroutines. I want to catch errors using CoroutineExceptionHandler, but I get the error:

E/[Koin]: Instance creation error : could not create instance for [Factory:'com.test.presentation.viewModel.ActivateDeviceViewModel']: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter context
        kotlin.coroutines.CombinedContext.plus(Unknown Source:2)
        kotlinx.coroutines.CoroutineContextKt.newCoroutineContext(CoroutineContext.kt:33)
        kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:52)
        kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
        kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
        kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
        com.test.presentation.viewModel.ActivateDeviceViewModel.fetchDevices(ActivateDeviceViewModel.kt:42)
        com.test.presentation.viewModel.ActivateDeviceViewModel.<init>(ActivateDeviceViewModel.kt:29)
        com.test.di.ModulesKt$viewModelModule$1$2.invoke(Modules.kt:63)
        com.test.di.ModulesKt$viewModelModule$1$2.invoke(Modules.kt:63)
        org.koin.core.instance.InstanceFactory.create(InstanceFactory.kt:51)
        org.koin.core.instance.FactoryInstanceFactory.get(FactoryInstanceFactory.kt:36)
        org.koin.core.registry.InstanceRegistry.resolveInstance$koin_core(InstanceRegistry.kt:103)
        org.koin.core.scope.Scope.resolveInstance(Scope.kt:236)
        org.koin.core.scope.Scope.access$resolveInstance(Scope.kt:34)
        org.koin.core.scope.Scope$get$1.invoke(Scope.kt:199)
        org.koin.core.time.MeasureKt.measureDurationForResult(Measure.kt:75)
        org.koin.core.scope.Scope.get(Scope.kt:198)
        com.test.presentation.fragment.ActivateDeviceFragment$special$$inlined$inject$default$1.invoke(ComponentCallbackExt.kt:69)
        kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        com.test.presentation.fragment.ActivateDeviceFragment.getViewModel(ActivateDeviceFragment.kt:39)
        com.test.presentation.fragment.ActivateDeviceFragment.access$getViewModel(ActivateDeviceFragment.kt:37)
        com.test.presentation.fragment.ActivateDeviceFragment$handleActivateDeviceResult$1$1.invokeSuspend(ActivateDeviceFragment.kt:51)
        kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        androidx.lifecycle.DispatchQueue.drainQueue(DispatchQueue.kt:75)
        androidx.lifecycle.DispatchQueue.resume(DispatchQueue.kt:54)
        androidx.lifecycle.LifecycleController$observer$1.onStateChanged(LifecycleController.kt:40)
        androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
        androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:265)
        androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:307)
        androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
        androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
        androidx.fragment.app.Fragment.performStart(Fragment.java:3024)
        androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:568)
        androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:277)
        androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
        androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1327)
        androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2757)
        androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2707)
        androidx.fragment.app.Fragment.performStart(Fragment.java:3028)
        androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:568)
        androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:277)
        androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
        androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1327)
        androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2757)
        androidx.fragm. 

Please tell me what am I doing wrong and how to fix it? Thanks

fragment

class ActivateDeviceFragment : Fragment(R.layout.fragment_activate_device) {
    private val viewModel: ActivateDeviceViewModel by inject()
    // ....
} 

view model

class ActivateDeviceViewModel constructor(
    private val activateDeviceUseCase: ActivateDeviceUseCase,
) : ViewModel() {

    init {
        fetchDevices()
    }

    private val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        Timber.tag("test").d(throwable)
    }

    private fun fetchDevices() {
        viewModelScope.launch(exceptionHandler) {
        }
    }
}

koin modules

val useCaseModule = module { 
    single { ActivateDeviceUseCase() }
}

val viewModelModule = module {
    viewModel { ActivateDeviceViewModel(get() }
}
like image 494
Tomas Avatar asked Jan 20 '26 21:01

Tomas


1 Answers

It is not related to Koin.

The log shows that the exceptionHandler in viewModelScope.launch(exceptionHandler) is actually null, and which throws NullPointerException.

It is because you trigger the fetchDevices() inside init{}, in which the instantiation of exceptionHandler is not ready (means it is null).

Therefore, you should not call fetchDevices() inside init{}. For example, you could call this in your Fragment onViewCreated(){ viewModel.fetchDevices } instead. It will be fine.

like image 104
zmartboz Avatar answered Jan 22 '26 15:01

zmartboz