Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CRASH][ANDROID] NullPointerException in getDeviceState #1159

Closed
diego-paired opened this issue Feb 6, 2021 · 32 comments · Fixed by #1193
Closed

[CRASH][ANDROID] NullPointerException in getDeviceState #1159

diego-paired opened this issue Feb 6, 2021 · 32 comments · Fixed by #1193

Comments

@diego-paired
Copy link

diego-paired commented Feb 6, 2021

Description:
We're receiving a number of crash reports originating in RNOneSignal.getDeviceState, stack trace attached below.

It affects every android SDK version and manufacturer.

Environment
"react-native-onesignal": "^4.0.2",
react: 17.0.1 => 17.0.1 react-native: 0.64.0-rc.2 => 0.64.0-rc.2

Steps to Reproduce Issue:
Haven't been able to reproduce the issue, it's reported on Google Play's crash dashboard.

Anything else:

  at com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState (RNOneSignal.java:4)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:147)
  at com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java:21)
  at com.facebook.react.bridge.queue.NativeRunnable.run (NativeRunnable.java)
  at android.os.Handler.handleCallback (Handler.java:883)
  at android.os.Handler.dispatchMessage (Handler.java:100)
  at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java)
  at android.os.Looper.loop (Looper.java:237)
  at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:37)
  at java.lang.Thread.run (Thread.java:919)
@ild-nucleusstudio
Copy link

+1.

java.lang.NullPointerException: at com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState (RNOneSignal.java:4) at java.lang.reflect.Method.invoke (Method.java) at com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:147) at com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java:21) at com.facebook.react.bridge.queue.NativeRunnable.run (NativeRunnable.java) at android.os.Handler.handleCallback (Handler.java:873) at android.os.Handler.dispatchMessage (Handler.java:99) at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java) at android.os.Looper.loop (Looper.java:224) at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:37) at java.lang.Thread.run (Thread.java:764)

@rgomezp
Copy link
Contributor

rgomezp commented Feb 8, 2021

Howdy Diego,
Thank you for reporting this issue.

Can you please include the code you're using? Also can you please provide environment info (e.g: device make and model, OS, etc...)?

Cheers

@diego-paired
Copy link
Author

Howdy Diego,
Thank you for reporting this issue.

Can you please include the code you're using? Also can you please provide environment info (e.g: device make and model, OS, etc...)?

Cheers

Thanks for your reply.

It's being reported on our Google Play console at the rate of about 50 crashes per week, I don't have any way to reproduce it.

image

@renatobentorocha
Copy link

renatobentorocha commented Feb 9, 2021

Same here.

info Fetching system and libraries information...
System:
OS: macOS 10.15.7
CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
Memory: 36.86 MB / 8.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 10.22.0 - ~/.nvm/versions/node/v10.22.0/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.6 - ~/.nvm/versions/node/v10.22.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.0 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 13.6, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
Android SDK:
API Levels: 23, 25, 26, 28, 29
Build Tools: 28.0.3, 29.0.2, 29.0.3
Android NDK: Not Found
IDEs:
Android Studio: 3.6 AI-192.7142.36.36.6308749
Xcode: 11.6/11E708 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_242 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.13.1 => 16.13.1
react-native: 0.63.3 => 0.63.3
react-native-macos: Not Found
npmGlobalPackages:
react-native: Not Found

"react-native": "0.63.3",
"react-native-onesignal": "^4.0.1",

Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'org.json.JSONObject com.onesignal.OSDeviceState.toJSONObject()' on a null object reference

com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState (RNOneSignal.java:239)
java.lang.reflect.Method.invoke (Method.java)
com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:372)
com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java:151)
com.facebook.react.bridge.queue.NativeRunnable.run (NativeRunnable.java)
android.os.Handler.handleCallback (Handler.java:883)
android.os.Handler.dispatchMessage (Handler.java:100)
com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java:27)
android.os.Looper.loop (Looper.java:241)
com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:226)
java.lang.Thread.run (Thread.java:919)

@rgomezp
Copy link
Contributor

rgomezp commented Feb 10, 2021

Please provide a code snippet in order to facilitate reproduction.

Cheers

@renatobentorocha
Copy link

renatobentorocha commented Feb 12, 2021

@rgomezp

The error occur in OneSignal.getDeviceState() call:

const getDeviceState =  async () => {
     const deviceState = await OneSignal.getDeviceState();
     console.log({ deviceState });
}

The exception is:

Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'org.json.JSONObject com.onesignal.OSDeviceState.toJSONObject()' on a null object reference

com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState (RNOneSignal.java:239)
java.lang.reflect.Method.invoke (Method.java)
com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java:372)
com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java:151)
com.facebook.react.bridge.queue.NativeRunnable.run (NativeRunnable.java)
android.os.Handler.handleCallback (Handler.java:883)
android.os.Handler.dispatchMessage (Handler.java:100)
com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java:27)
android.os.Looper.loop (Looper.java:241)
com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java:226)
java.lang.Thread.run (Thread.java:919)

The OneSignal and React Native packages are:

"react-native": "0.63.3",
"react-native-onesignal": "^4.0.1",

The react native info:

info Fetching system and libraries information...
System:
OS: macOS 10.15.7
CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
Memory: 36.86 MB / 8.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 10.22.0 - ~/.nvm/versions/node/v10.22.0/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.6 - ~/.nvm/versions/node/v10.22.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.0 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 13.6, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
Android SDK:
API Levels: 23, 25, 26, 28, 29
Build Tools: 28.0.3, 29.0.2, 29.0.3
Android NDK: Not Found
IDEs:
Android Studio: 3.6 AI-192.7142.36.36.6308749
Xcode: 11.6/11E708 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_242 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.13.1 => 16.13.1
react-native: 0.63.3 => 0.63.3
react-native-macos: Not Found
npmGlobalPackages:
react-native: Not Found

@dijinshopwise
Copy link

Getting same issue.

"react-native": "^0.63.4",
"react-native-onesignal": "^4.0.3"

I upgraded from "react-native-onesignal": "^3.9.3" to "react-native-onesignal": "^4.0.3". I am getting crashes now. I have updated the init code same as above. I added freshly.

...
let device = await OneSignal.getDeviceState();
...

From Play Store Crash Log

java.lang.NullPointerException:
at com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState (RNOneSignal.java)
at java.lang.reflect.Method.invoke (Method.java)
at com.facebook.react.bridge.JavaMethodWrapper.invoke (JavaMethodWrapper.java)
at com.facebook.react.bridge.JavaModuleWrapper.invoke (JavaModuleWrapper.java)
at com.facebook.react.bridge.queue.NativeRunnable.run (NativeRunnable.java)
at android.os.Handler.handleCallback (Handler.java:739)
at android.os.Handler.dispatchMessage (Handler.java:95)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage (MessageQueueThreadHandler.java)
at android.os.Looper.loop (Looper.java:148)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run (MessageQueueThreadImpl.java)
at java.lang.Thread.run (Thread.java:818)

Firebase Crash log

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'org.json.JSONObject com.onesignal.g0.a()' on a null object reference
at com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState(RNOneSignal.java:4)
at java.lang.reflect.Method.invoke(Method.java)

@rgomezp
Copy link
Contributor

rgomezp commented Feb 17, 2021

Howdy,
Thank you for the information, but if someone could provide a code example that would be most helpful. This

const getDeviceState = async () => {
const deviceState = await OneSignal.getDeviceState();
console.log({ deviceState });
}

doesn't quite provide enough information to reproduce. What we need is some more context as to how and where this is being used.

E.g: how does the call to getDeviceState relate to the OneSignal initialization? or what RN lifecycle function is this being called in?

Please make sure to provide all the context necessary to resolve this since the stack traces provided don't quite provide what we need.

I suspect this might be happening if getDeviceState is called too quickly. Can you try moving getDeviceState to a point in your code that is guaranteed to run after OneSignal initialization completes? e.g: put it in the callback to the subscription change observer.

Remember that getDeviceState captures a snapshot at the time it is called. For that reason do not cache the state, but rather re-get it as needed.

Reference

@renatobentorocha
Copy link

@rgomezp

Hi, in the App.tsx, our entry point, we have:

const oneSignalIntialize = async () => {
  OneSignal.setAppId('xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx');
}

const App = () => {
  useEffect(() => {
    oneSignalIntialize();

  }, []);

return (
    <LoginFlow />
);

}

After the login flow, if this was succeed, we have a welcome screen:

const getDeviceState = async () => {
    const deviceState = await OneSignal.getDeviceState(); 
    console.log({ deviceState });
}

const WelcomeScreen = () => {
  useEffect(() => {
    getDeviceState();

  }, []);

return (
    <Components />
);

}

Screen Shot 2021-02-18 at 10 59 35

@tyang1
Copy link

tyang1 commented Feb 21, 2021

Hi @renatobentorocha ,
Thank you for sharing the code with us!
The useEffect hook here may be called to initialize the SDK within the main App component. This is essentially an async call that gets called as soon as the component is mounted, but there's no state that checks if the initialization has been completed.

You can use the useState hook to add a state to check for the init status before rendering the Welcome screen.

Another recommendation, as suggested by @rgomezp , would be to run it in the callback to the addSubscriptionObserver (sorry for me speaking too quickly on the deprecated getPermissionSubscriptionState earlier), which would guarantee that the SDK has been successfully initialized before accessing the device state.

Let us know if this helps resolving the issue,

Thanks


@renatobentorocha
Copy link

renatobentorocha commented Feb 22, 2021

Hi @tyang1 Thanks by attention, but the one-signal docs for that callback (getPermissionSubscriptionState) say:

Screen Shot 2021-02-22 at 09 07 47

I will test with addSubscriptionObserver and will let you know what happen

@oferRounds
Copy link

oferRounds commented Feb 22, 2021

Getting this also. Don’t know how to reproduce. @rgomezp perhaps it’s a threading issue? This happens randomly, but quite a lot. We see many traces for in our crash report

@oferRounds
Copy link

image

@rgomezp
Copy link
Contributor

rgomezp commented Feb 22, 2021

Howdy,
I don't think this is a threading issue as the stack traces point to it more likely occurring at the wrapper level. This could certainly be a bug. We just need reliable reproduction steps to move forward with a resolution.

It is interesting @renatobentorocha that you report it is happening 98% in background.

@oferRounds @dijinshopwise is this something you are also seeing?

@dan-developer
Copy link

+1

@dan-developer
Copy link

image

@dan-developer
Copy link

@rgomezp follow the code:

async componentDidMount() {
        this._isMounted = true

        /* O N E S I G N A L   S E T U P */
        OneSignal.setAppId('***********************************')
        OneSignal.setLogLevel(0, 0)
        OneSignal.setRequiresUserPrivacyConsent(false)

        /* O N E S I G N A L  H A N D L E R S */
        OneSignal.setNotificationWillShowInForegroundHandler(notifReceivedEvent => {
            let notification = notifReceivedEvent.getNotification()

            const buttonOk = {
                text: 'OK', onPress: () => {
                    notifReceivedEvent.complete()
                },
            }

            Alert.alert(notification.title, notification.body, [buttonOk])

            if (notification.additionalData) {
                //
            }

        })
        OneSignal.setNotificationOpenedHandler(notification => {
            //
        })


        OneSignal.disablePush(true)

        const deviceState = await OneSignal.getDeviceState()

        if (Platform.OS === 'ios' && !deviceState.isSubscribed) {
            OneSignal.promptForPushNotificationsWithUserResponse(function(accepted) {
                //
            });
        }
		
		// ...
}

@dan-developer
Copy link

I downgrade to old version and Google Play warns about same error.

com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState

@dan-developer
Copy link

Any suggestion? Any temporary solution?

@diego-paired
Copy link
Author

We're still receiving hundreds of these crashes, any news?
image

@oferRounds
Copy link

@diego-paired our temporary workaround was to delay the call to getDeviceState() after OneSingal init (we added 2 seconds delay)

@diego-paired
Copy link
Author

@diego-paired our temporary workaround was to delay the call to getDeviceState() after OneSingal init (we added 2 seconds delay)

thanks for the tip

@renatobentorocha
Copy link

Hi @renatobentorocha ,
Thank you for sharing the code with us!
The useEffect hook here may be called to initialize the SDK within the main App component. This is essentially an async call that gets called as soon as the component is mounted, but there's no state that checks if the initialization has been completed.

You can use the useState hook to add a state to check for the init status before rendering the Welcome screen.

Another recommendation, as suggested by @rgomezp , would be to run it in the callback to the addSubscriptionObserver (sorry for me speaking too quickly on the deprecated getPermissionSubscriptionState earlier), which would guarantee that the SDK has been successfully initialized before accessing the device state.

Let us know if this helps resolving the issue,

Thanks

@diego-paired @dan-developer

I followed the @rgomezp suggestion and the crashes, for now, stopped occur.

OneSignal.addSubscriptionObserver(() => {
    OneSignal.getDeviceState().then((deviceState) => {
      console.log({deviceState})
    });
  });

@jfishman1
Copy link

Using the test app, added this code to a button:

Situation 1

let getDeviceStateButton = this.renderButtonView(
"Get Device State",
isExternalUserIdLoading || isPrivacyConsentLoading,
() => {

console.log("Attempting to get device state");
this.setState({ isExternalUserIdLoading: true }, () => {
// OneSignal getDeviceState
let deviceState = OneSignal.getDeviceState();
console.log("deviceState: ", deviceState);
console.log("deviceState.isSubscribed: ", deviceState.isSubscribed);
this.setState({ isExternalUserIdLoading: false, isSubscribed: deviceState.isSubscribed });
console.log("this.state.isSubscribed after calling setState: ", this.state.isSubscribed);
console.log("deviceState.userId: ", deviceState.userId)
});
});

Log shows:

2021-03-25 20:06:14.589544-0700 jonexample[751:27269] [javascript] Attempting to get device state
2021-03-25 20:06:14.645290-0700 jonexample[751:27269] [javascript] 'deviceState: ', { _U: 0, _V: 0, _W: null, _X: null }
2021-03-25 20:06:14.647385-0700 jonexample[751:27269] [javascript] 'deviceState.isSubscribed: ', undefined
2021-03-25 20:06:14.647768-0700 jonexample[751:27269] [javascript] 'this.state.isSubscribed after calling setState: ', undefined
2021-03-25 20:06:14.647897-0700 jonexample[751:27269] [javascript] 'deviceState.userId: ', undefined

However when using the example code provided in the componentDidMount:

Situation 2

async componentDidMount(){
 const deviceState = await OneSignal.getDeviceState();
this.setState({ isSubscribed : deviceState.isSubscribed});
console.log("componentDidMount deviceState: ", deviceState);
console.log("componentDidMount deviceState.isSubscribed: ", deviceState.isSubscribed);
console.log("componentDidMount deviceState.userId: ", deviceState.userId);
}

The log shows:

2021-03-25 20:14:46.521099-0700 jonexample[1326:55465] [javascript] 'componentDidMount deviceState: ', { userId: '92aa2978-3f53-454e-b1a6-652c43f0dba4',
2021-03-25 20:14:46.521328-0700 jonexample[1326:55465] [javascript] 'componentDidMount deviceState.isSubscribed: ', true
2021-03-25 20:14:46.521435-0700 jonexample[1326:55465] [javascript] 'componentDidMount deviceState.userId: ', '92aa2978-3f53-454e-b1a6-652c43f0dba4'

@rgomezp is scenario 1 expected behavior?

@diego-paired @renatobentorocha @dan-developer could y'all share your examples or confirm if both above situations is what you see?

Please be detailed in how you are using this method and include steps for us to reproduce.

@rgomezp
Copy link
Contributor

rgomezp commented Mar 31, 2021

@jfishman1 Take a look at the documentation. You will see that getDeviceState is an asynchronous function so make sure to await on it, or to use a then.

Regardless, it appears there's a good opportunity to handle this in the SDK to avoid these crashes. Thanks for surfacing. We will explore what changes can be made to more seamlessly handle these.

@rgomezp
Copy link
Contributor

rgomezp commented Mar 31, 2021

Digging into this a bit further, it appears that the most likely cause of this is the native side is trying to get the device state before the context has loaded.

if (appContext == null) {
         logger.error("OneSignal.initWithContext has not been called. Could not get OSDeviceState");
         return null;
      }

source

To confirm, please look for OneSignal.initWithContext has not been called. Could not get OSDeviceState in your native logcat (e.g: open Android Studio).

In the meantime, it seems we can add a check in the Android bridge to prevent a full on crash.

Regardless, please note:
We still recommend only getting the device state via the subscription change observer to ensure you're not getting the device state too early

Thanks for your patience y'all.


From #1085:
Instead of delaying the device state getter via a timer, try putting the getDeviceState function call in the subscription observer. This way, you will know that it is subscribed. e.g:

OneSignal.addSubscriptionObserver(async (event) => {
    this.OSLog("OneSignal: subscription changed:", event);
    if (event.to.isSubscribed) {
        const state = await OneSignal.getDeviceState();
        // do something with the device state
    }
});

@rgomezp
Copy link
Contributor

rgomezp commented Apr 22, 2021

Update: this is now fixed in the latest version 4.0.7

@EnginYilmaz
Copy link

EnginYilmaz commented Apr 27, 2021

I updated to the latest 4.0.7 repo. I am not sure if I am asking on the right place but on some iOS simulators and Android devices I even only get these fields with deviceState for example like this one. There is even no userId field.

{"rootTag":21,"initialProps":{}}
 WARN  {"hasNotificationPermission": false, "isEmailSubscribed": false, "isPushDisabled": false, "isSMSSubscribed": false, "isSubscribed": false, "notificationPermissionStatus": 0}

@bhaktitud
Copy link

Digging into this a bit further, it appears that the most likely cause of this is the native side is trying to get the device state before the context has loaded.

if (appContext == null) {
         logger.error("OneSignal.initWithContext has not been called. Could not get OSDeviceState");
         return null;
      }

source

To confirm, please look for OneSignal.initWithContext has not been called. Could not get OSDeviceState in your native logcat (e.g: open Android Studio).

In the meantime, it seems we can add a check in the Android bridge to prevent a full on crash.

Regardless, please note: We still recommend only getting the device state via the subscription change observer to ensure you're not getting the device state too early

Thanks for your patience y'all.

From #1085: Instead of delaying the device state getter via a timer, try putting the getDeviceState function call in the subscription observer. This way, you will know that it is subscribed. e.g:

OneSignal.addSubscriptionObserver(async (event) => {
    this.OSLog("OneSignal: subscription changed:", event);
    if (event.to.isSubscribed) {
        const state = await OneSignal.getDeviceState();
        // do something with the device state
    }
});

I've tried that but it is not 100% work, like sometimes i got the device id sometimes not, mostly the negative case happens when on the first launch of the app. So i have to close the app first, then the device id will be fetch successfully.
any idea?

@EnginYilmaz
Copy link

EnginYilmaz commented Jul 28, 2022

Is there any body still getting this error in 28 July 2022 or only me?

@anwersolangi
Copy link

Is there any body still getting this error in 28 July 2022 or only me?

Getting the same error. However not often or rarely on some of devices like Redmi and Infinix.

@MursiDirect
Copy link

Same issue here, any solution yet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.