russhwolf / multiplatform-settings

A Kotlin Multiplatform library for saving simple key-value data
Apache License 2.0
1.57k stars 67 forks source link

[Android] Nullpointer exception while creating Settings instance #127

Open ismkhanh opened 1 year ago

ismkhanh commented 1 year ago

We are receiving crash while creating instance of Settings on Android. Looks its not able to get context from androidx-startup ? version: 0.8

Caused by java.lang.NullPointerException
       at com.russhwolf.settings.NoArgKt.Settings(NoArgKt.java:33)
russhwolf commented 1 year ago

Seems weird, but that doesn't really give me enough information to understand what's going on. Since 0.8 is pretty old, can you try it on a newer version such as 1.0.0-alpha01?

DimitrisKatsamagos commented 1 year ago

@russhwolf regarding the issue raised, i have a kmm app that i try to test with robolectric from android module (android/src/test). I wanna test the Main Activity but even the simplest test like assertNotNull(activity) crashes with the same error. I have upgraded to latest version 1.0.0-alpha01.

And the stack trace below

java.lang.NullPointerException at com.russhwolf.settings.NoArgKt.Settings(NoArg.kt:32) at com.calendrop.app.service.SettingsRepository$Companion$instance$2.invoke(SettingsRepository.kt:11) at com.calendrop.app.service.SettingsRepository$Companion$instance$2.invoke(SettingsRepository.kt:10) at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74) at com.calendrop.app.service.SettingsRepository$Companion.getInstance(SettingsRepository.kt:10) at com.calendrop.common.ui.viewmodel.LanguageSettings.(LanguageSettings.kt:27) at com.calendrop.common.ui.viewmodel.LanguageSettings$Companion.getInstance(LanguageSettings.kt:21) at com.calendrop.common.ui.utils.GetResourcesKt.SetStoredLanguage(GetResources.kt:49) at com.calendrop.common.ui.utils.GetResourcesKt.SetupApplicationLanguage(GetResources.kt:44) at com.calendrop.common.ui.SplashScreenUINewKt.SplashScreenContentNew(SplashScreenUINew.kt:25) at com.calendrop.common.ui.utils.RootUIKt$SplashContentNew$1.invoke(RootUI.kt:50) at com.calendrop.common.ui.utils.RootUIKt$SplashContentNew$1.invoke(RootUI.kt:48) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt$Children$1$1.invoke(Children.kt:31) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt$Children$1$1.invoke(Children.kt:30) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt$Children$1.invoke(Children.kt:30) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt$Children$1.invoke(Children.kt:29) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.DefaultStackAnimation$invoke$1$1$2.invoke(DefaultStackAnimation.kt:55) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.DefaultStackAnimation$invoke$1$1$2.invoke(DefaultStackAnimation.kt:53) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.ComposableSingletons$FadeKt$lambda-1$1.invoke(Fade.kt:17) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.ComposableSingletons$FadeKt$lambda-1$1.invoke(Fade.kt:16) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:149) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.DefaultStackAnimator.invoke(DefaultStackAnimator.kt:43) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.animation.DefaultStackAnimation.invoke(DefaultStackAnimation.kt:42) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt.Children(Children.kt:29) at com.arkivanov.decompose.extensions.compose.jetbrains.stack.ChildrenKt.Children(Children.kt:46) at com.calendrop.common.ui.utils.RootUIKt.SplashContentNew(RootUI.kt:45) at com.calendrop.android.MainActivity$onCreate$1$1.invoke(MainActivity.kt:33) at com.calendrop.android.MainActivity$onCreate$1$1.invoke(MainActivity.kt:32) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at com.calendrop.app.theme.AppThemeKt$AppTheme$1.invoke(AppTheme.kt:40) at com.calendrop.app.theme.AppThemeKt$AppTheme$1.invoke(AppTheme.kt:39) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at com.calendrop.app.theme.AppThemeKt.AppTheme(AppTheme.kt:33) at com.calendrop.android.MainActivity$onCreate$1.invoke(MainActivity.kt:32) at com.calendrop.android.MainActivity$onCreate$1.invoke(MainActivity.kt:27) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:404) at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:250) at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:249) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:177) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:123) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:122) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:114) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:157) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:156) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:156) at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:140) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3248) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3238) at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341) at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3238) at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3173) at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:587) at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:950) at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131) at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1060) at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:131) at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:182) at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360) at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:202) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:138) at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131) at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1147) at android.view.View.dispatchAttachedToWindow(View.java:20753) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3490) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2613) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2126) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8649) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1037) at android.view.Choreographer.doCallbacks(Choreographer.java:845) at android.view.Choreographer.doFrame(Choreographer.java:780) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at org.robolectric.shadows.ShadowPausedLooper$IdlingRunnable.run(ShadowPausedLooper.java:368) at org.robolectric.shadows.ShadowPausedLooper.executeOnLooper(ShadowPausedLooper.java:402) at org.robolectric.shadows.ShadowPausedLooper.idle(ShadowPausedLooper.java:93) at org.robolectric.shadows.ShadowPausedLooper.idleIfPaused(ShadowPausedLooper.java:164) at org.robolectric.android.controller.ActivityController.visible(ActivityController.java:208) at org.robolectric.android.controller.ActivityController.setup(ActivityController.java:319) at com.calendrop.android.TestApplication.setUp(TestApplication.kt:47) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54) at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:148) at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:147) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.withDisposableContent(ComposeUiTest.android.kt:476) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:294) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTextInputService(ComposeUiTest.android.kt:360) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTextInputService(ComposeUiTest.android.kt:217) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:293) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withComposeIdlingResource(ComposeUiTest.android.kt:347) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withComposeIdlingResource(ComposeUiTest.android.kt:217) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(ComposeUiTest.android.kt:292) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withWindowRecomposer(ComposeUiTest.android.kt:321) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withWindowRecomposer(ComposeUiTest.android.kt:217) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1.invoke(ComposeUiTest.android.kt:291) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTestCoroutines(ComposeUiTest.android.kt:334) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTestCoroutines(ComposeUiTest.android.kt:217) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1.invoke(ComposeUiTest.android.kt:290) at androidx.compose.ui.test.junit4.IdlingStrategy.withStrategy(IdlingStrategy.android.kt:52) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(ComposeUiTest.android.kt:289) at androidx.compose.ui.test.junit4.IdlingResourceRegistry.withRegistry(IdlingResourceRegistry.jvm.kt:157) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1.invoke(ComposeUiTest.android.kt:288) at androidx.compose.ui.test.junit4.ComposeRootRegistry.withRegistry(ComposeRootRegistry.android.kt:146) at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(ComposeUiTest.android.kt:287) at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1.evaluate(AndroidComposeTestRule.android.kt:147) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:591) at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:274) at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:88) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:829)

russhwolf commented 1 year ago

I don't think androidx.startup gets initialized in a Robolectric test. There's not really much Multiplatform Settings can do there.

I could think about adding an API where you could inject your test Context the way the library does in its tests. But if you're going to use something like that, you might as well just inject the Settings instance instead and avoid calling the no-arg constructor from your tests in the first place.

viktor-savchik-idf commented 1 year ago

I have the same issue, and I've tried the new 1.0.0-RC version but unsuccessfully.

filpgame commented 1 year ago

Same here, the merged manifest seems ok, the androidx.startup.InitializationProvider and com.russhwolf.settings.SettingsInitializer is there, but it appears the SettingsInitializer isn't receiving the context. I'm using 1.0.0

FATAL EXCEPTION: main
Process: com.stone.tapsdk:TLniWnlh6DxccKfljv7c, PID: 25927
java.lang.RuntimeException: Unable to create application com.example.app.DemoApplication: java.lang.NullPointerException
    at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7000)
    at android.app.ActivityThread.access$1500(ActivityThread.java:258)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1983)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:236)
    at android.app.ActivityThread.main(ActivityThread.java:8061)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
Caused by: java.lang.NullPointerException
    at com.russhwolf.settings.NoArgKt.Settings(NoArg.kt:32)
    at co.stone.commons.InitiatorTransactionKeyHandler.<init>(InitiatorTransactionKeyHandler.kt:9)
    at com.example.lib.TapPciMpp.<init>(TapPciMpp.kt:41)
    at com.example.lib.TapSdk.selectLib(TapSdk.kt:20)
    at com.example.lib.TapSdk.initialize(TapSdk.kt:27)
    at com.example.app.DemoApplication.onCreate(DemoApplication.kt:9)
    at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1193)
russhwolf commented 1 year ago

Again, I don't expect this to work from a Robolectric test because I don't think Robolectric correctly initializes ContentProviders like androidx.startup uses. If you need to test code that talks to your Settings object, pass in a MapSettings instead of calling the no-arg constructor.

Ustimov commented 1 year ago

I had the same issue in my app with multiple processes, so I fixed it with an explicit call of the library initializer in my Application class implementation:

AppInitializer.getInstance(getApplicationContext()).initializeComponent(SettingsInitializer.class);
russhwolf commented 1 year ago

Oh that's interesting. SettingsInitializer is marked internal, so that only works from Java code, but maybe the right solution here is to just make it public.

russhwolf commented 8 months ago

I plan to make SettingsInitializer public in version 1.2.0 so that you have more flexibility to handle this in tests.

jmadaminov commented 1 week ago

I'm also having the same error when I try to initialize my kmm library(which uses settings-no-arg) in my android App's onCreate method

russhwolf commented 6 days ago

SettingsInitializer is now public in version 1.2.0. I'm interested to hear whether this solves or allows workaround for your crashes.