React-native-onesignal: [崩溃][ANDROID] getDeviceState 中的 NullPointerException

创建于 2021-02-06  ·  28评论  ·  资料来源: OneSignal/react-native-onesignal

描述:
我们收到了许多源自RNOneSignal.getDeviceState的崩溃报告,堆栈跟踪如下。

它会影响每个 android SDK 版本和制造商。

环境
"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

重现问题的步骤:
无法重现该问题,已在 Google Play 的崩溃仪表板上报告。

还要别的吗:

  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)
Android Possible Bug

最有用的评论

@diego-paired 我们的临时解决方法是在 OneSingal init 之后延迟对getDeviceState()调用(我们添加了 2 秒延迟)

谢谢你的提示

所有28条评论

+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)

你好,迭戈
感谢您报告此问题。

你能包括你正在使用的代码吗? 能否请您提供环境信息(例如:设备品牌和型号、操作系统等...)?

干杯

你好,迭戈
感谢您报告此问题。

你能包括你正在使用的代码吗? 能否请您提供环境信息(例如:设备品牌和型号、操作系统等...)?

干杯

感谢您的回复。

它以每周大约 50 次崩溃的速度在我们的 Google Play 控制台上报告,我无法重现它。

image

同样在这里。

info 正在获取系统和图书馆信息...
系统:
操作系统:macOS 10.15.7
CPU:(4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
内存:36.86 MB / 8.00 GB
外壳:5.7.1 - /bin/zsh
二进制文件:
节点:10.22.0 - ~/.nvm/versions/node/v10.22.0/bin/node
纱线:1.22.4 - /usr/local/bin/yarn
npm: 6.14.6 - ~/.nvm/versions/node/v10.22.0/bin/npm
守望者:4.9.0 - /usr/local/bin/watchman
经理:
CocoaPods:1.10.0 - /usr/local/bin/pod
SDK:
IOS SDK:
平台:iOS 13.6、DriverKit 19.0、macOS 10.15、tvOS 13.4、watchOS 6.2
安卓SDK:
API 级别:23、25、26、28、29
构建工具:28.0.3、29.0.2、29.0.3
Android NDK:未找到
IDE:
安卓工作室:3.6 AI-192.7142.36.36.6308749
Xcode: 11.6/11E708 - /usr/bin/xcodebuild
语言:
Java:1.8.0_242 - /usr/bin/javac
Python:2.7.16 - /usr/bin/python
npm 软件包:
@react-native-community/cli:未找到
反应:16.13.1 => 16.13.1
反应原生:0.63.3 => 0.63.3
react-native-macos:未找到
npmGlobalPackages:
反应原生:未找到

“反应原生”:“0.63.3”,
"react-native-onesignal": "^4.0.1",

致命异常:java.lang.NullPointerException
尝试在空对象引用上调用虚拟方法“org.json.JSONObject com.onesignal.OSDeviceState.toJSONObject()”

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

错误发生在 OneSignal.getDeviceState() 调用中:

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

例外是:

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)

OneSignal 和 React Native 包是:

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

The react native info:

```typescript
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

得到同样的问题。

“反应原生”:“^0.63.4”,
"react-native-onesignal": "^4.0.3"

我从“react-native-onesignal”:“^3.9.3”升级到“react-native-onesignal”:“^4.0.3”。 我现在崩溃了。 我已经更新了与上面相同的初始化代码。 我是新鲜添加的。

...
让设备 = 等待 OneSignal.getDeviceState();
...

从 Play 商店崩溃日志

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

Firebase 崩溃日志

致命异常:java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“org.json.JSONObject com.onesignal.g0.a()”
在 com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState(RNOneSignal.java:4)
在 java.lang.reflect.Method.invoke(Method.java)

你好,
感谢您提供信息,但如果有人可以提供最有帮助的代码示例。 这

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

并没有提供足够的信息来重现。 我们需要的是更多关于如何以及在哪里使用它的上下文。

例如:对getDeviceState的调用与 OneSignal 初始化有何关系? 或者这是在调用什么 RN 生命周期函数?

请确保提供解决此问题所需的所有上下文,因为提供的堆栈跟踪并不能完全满足我们的需要。

我怀疑如果getDeviceState被调用得太快,这可能会发生。 您可以尝试将getDeviceState到代码中保证在 OneSignal 初始化完成后运行的点吗? 例如:将其放入订阅更改观察者的回调中。

请记住, getDeviceState在调用时捕获快照。 出于这个原因,不要缓存状态,而是根据需要重新获取它。

参考

@rgomezp

您好,在我们的入口点 App.tsx 中,我们有:

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

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

  }, []);

return (
    <LoginFlow />
);

}

在登录流程之后,如果成功,我们会有一个欢迎屏幕:

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

@renatobentorocha
感谢您与我们分享代码!
可以调用这里的 useEffect 钩子来初始化 App 主组件中的 SDK。 这本质上是一个异步调用,在组件安装后立即调用,但没有状态检查初始化是否已完成。
有的
您可以使用 useState 钩子添加状态以在呈现欢迎屏幕之前检查初始化状态。
有的
正如@rgomezp所建议的addSubscriptionObserver的回调中运行它(对不起,我之前对已弃用的 getPermissionSubscriptionState 说得太快了),这将保证 SDK 在访问设备状态之前已成功初始化。
有的
让我们知道这是否有助于解决问题,
有的
谢谢
有的

@tyang1感谢关注,但该回调的单信号文档 (getPermissionSubscriptionState) 说:

Screen Shot 2021-02-22 at 09 07 47

我将用addSubscriptionObserver测试,并让您知道会发生什么

得到这个也。 不知道怎么复现。 @rgomezp可能是线程问题? 这种情况随机发生,但相当多。 我们在崩溃报告中看到了许多痕迹

image

你好,
我不认为这是一个线程问题,因为堆栈跟踪指向它更有可能发生在包装器级别。 这当然可能是一个错误。 我们只需要可靠的复制步骤来推进解决方案。

有趣的是@renatobentorocha ,你报告它在后台发生 98%。

@oferRounds @dijinshopwise这也是你看到的吗?

+1

image

@rgomezp跟随代码:

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) {
                //
            });
        }

        // ...
}

我降级到旧版本,谷歌播放警告同样的错误。

com.geektime.rnonesignalandroid.RNOneSignal.getDeviceState

有什么建议吗? 有什么临时解决办法吗?

我们仍然收到数百起这样的崩溃,有什么消息吗?
image

@diego-paired 我们的临时解决方法是在 OneSingal init 之后延迟对getDeviceState()调用(我们添加了 2 秒延迟)

@diego-paired 我们的临时解决方法是在 OneSingal init 之后延迟对getDeviceState()调用(我们添加了 2 秒延迟)

谢谢你的提示

@renatobentorocha
感谢您与我们分享代码!
可以调用这里的 useEffect 钩子来初始化 App 主组件中的 SDK。 这本质上是一个异步调用,在组件安装后立即调用,但没有状态检查初始化是否已完成。
有的
您可以使用 useState 钩子添加状态以在呈现欢迎屏幕之前检查初始化状态。
有的
正如@rgomezp所建议的addSubscriptionObserver的回调中运行它(对不起,我之前对已弃用的 getPermissionSubscriptionState 说得太快了),这将保证 SDK 在访问设备状态之前已成功初始化。
有的
让我们知道这是否有助于解决问题,
有的
谢谢

@diego-paired @dan-developer

我遵循了@rgomezp 的建议,现在崩溃停止了。

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

使用test app ,将此代码添加到按钮:

情况一

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

日志显示:

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

但是,当使用componentDidMount 中提供

情况二

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

日志显示:

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是场景 1 的预期行为吗?

@diego-paired @renatobentorocha @dan-developer 能否分享您的示例或确认上述两种情况是否都是您所看到的?

请详细说明您如何使用此方法,并包括我们重现的步骤。

@jfishman1查看文档。 你会看到getDeviceState是一个异步函数,所以一定要等待它,或者使用then

无论如何,似乎有一个很好的机会在 SDK 中处理此问题以避免这些崩溃。 感谢您的浮出水面。 我们将探索可以进行哪些更改以更无缝地处理这些问题。

深入研究一下,似乎最可能的原因是本机端试图在上下文加载之前获取设备状态。

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

来源

要确认,请在您的本地 logcat 中查找OneSignal.initWithContext has not been called. Could not get OSDeviceState (例如:打开 Android Studio)。

与此同时,我们似乎可以在 Android 网桥中添加一个检查,以防止完全崩溃。

不管怎样,请注意:
我们仍然建议仅通过订阅更改观察器获取设备状态,以确保您不会过早获取设备状态

感谢大家的耐心等待。


来自#1085:
与其通过计时器延迟设备状态获取器,不如尝试将getDeviceState函数调用放入订阅观察者中。 这样,您就会知道它已被订阅。 例如:

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

更新:此问题现已在最新版本 4.0.7 中修复

我更新到最新的 4.0.7 repo。 我不确定我是否问对了地方,但在一些 iOS 模拟器和 Android 设备上,我什至只使用 deviceState 获取这些字段,例如像这样的。 甚至没有 userId 字段。

{"rootTag":21,"initialProps":{}}
 WARN  {"hasNotificationPermission": false, "isEmailSubscribed": false, "isPushDisabled": false, "isSMSSubscribed": false, "isSubscribed": false, "notificationPermissionStatus": 0}
此页面是否有帮助?
0 / 5 - 0 等级