Nativescript-vue: App crash when you receive push notification on background mode (or app is closed)

Created on 13 Nov 2018  路  5Comments  路  Source: nativescript-vue/nativescript-vue

Version

2.0.2

Reproduction link

https://github.com/academici/nativescript-vue-firebase

Platform and OS info

Android 7.0, Nativescript 5.0, ubuntu 18.04

Steps to reproduce

unknown

What is expected?

  1. when the app is active, messages received successfully
  2. when the app is background or closed, messages are displayed in top panel. When clicking on the message, the app opens with an error:
An uncaught Exception occurred on "main" thread.
com.tns.NativeScriptException: 
Calling js method onViewAttachedToWindow failed

Error: Cannot assign primitive value to array of objects.
File: "file:///data/data/ru.iwant.app/files/app/vendor.js", line: 43034, column: 26

StackTrace: 
    Frame: function:'', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43034, column: 27
    Frame: function:'javaObjectArray', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43033, column: 10
    Frame: function:'createDummyZeroDurationAnimator', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43040, column: 75
    Frame: function:'NoTransition.createAndroidAnimator', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43053, column: 12
    Frame: function:'setupExitAndPopEnterAnimation', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 42909, column: 33
    Frame: function:'setupAllAnimation', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 42922, column: 3
    Frame: function:'_setAndroidFragmentTransitions', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 42402, column: 5
    Frame: function:'Frame._navigateCore', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 44121, column: 28
    Frame: function:'FrameBase.performNavigation', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43381, column: 10
    Frame: function:'FrameBase._processNextNavigationEntry', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43370, column: 14
    Frame: function:'Frame._processNextNavigationEntry', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43969, column: 52
    Frame: function:'Frame._onAttachedToWindow', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43935, column: 10
    Frame: function:'AttachListener.onViewAttachedToWindow', file:'file:///data/data/ru.iwant.app/files/app/vendor.js', line: 43839, column: 17


    at com.tns.Runtime.callJSMethodNative(Native Method)
    at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1116)
    at com.tns.Runtime.callJSMethodImpl(Runtime.java:996)
    at com.tns.Runtime.callJSMethod(Runtime.java:983)
    at com.tns.Runtime.callJSMethod(Runtime.java:967)
    at com.tns.Runtime.callJSMethod(Runtime.java:959)
    at com.tns.gen.java.lang.Object_vendor_43830_28_AttachListener.onViewAttachedToWindow(Object_vendor_43830_28_AttachListener.java:17)
    at android.view.View.dispatchAttachedToWindow(View.java:15406)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2953)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2960)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2960)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2960)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2960)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2960)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1516)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1246)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6301)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
    at android.view.Choreographer.doCallbacks(Choreographer.java:683)
    at android.view.Choreographer.doFrame(Choreographer.java:619)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)


### What is actually happening?

normal

Most helpful comment

I was able to resolve by putting a JSON.stringify in the message console

onMessageReceivedCallback: message => {
   console.log("Push message received: ");
   console.log(JSON.stringify(message));
},

By clicking on the notification the problem of before disappeared!

:1st_place_medal:

All 5 comments

i have exactly the same problem, @academici did you fix this?

I was able to resolve by putting a JSON.stringify in the message console

onMessageReceivedCallback: message => {
   console.log("Push message received: ");
   console.log(JSON.stringify(message));
},

By clicking on the notification the problem of before disappeared!

:1st_place_medal:

@rubensfernandes, is my app.js

import { messaging, Message } from "nativescript-plugin-firebase/messaging";
import * as platform from "tns-core-modules/platform";
import * as applicationSettings from "tns-core-modules/application-settings";

new Vue({
  store,
  components: {
    StartPage
  },
  computed: {
    ...mapState({
      user: state => state.auth.user,
      role: state => state.auth.role
    }),
    tabsKey() {
      return `tabs-${this.role}`;
    }
  },
  template: `
    <Page @loaded="appLoaded">
      <GridLayout columns="*" rows="*, auto">
        <StackLayout row="0">
          <Frame>
            <StartPage></StartPage>
          </Frame>
        </StackLayout>
        <keep-alive>
          <app-bottombar row="1" @navigated="navigated"></app-bottombar>
        </keep-alive>
      </GridLayout>
    </Page>
  `,
  methods: {
    appLoaded(args) {
      this.$store.commit("SET_DEVICE_UUID", device.uuid);

      if (
        applicationSettings.getBoolean(
          "APP_REGISTERED_FOR_NOTIFICATIONS",
          false
        )
      ) {
        this.doRegisterPushHandlers();
      } else {
        this.doRegisterForPushNotifications();
      }
    },

    navigated(args) {
      if (args === "back") {
        this.$navigateBack();
      } else {
        this.$navigateTo(args.page, args.options);
      }
    },

    doRegisterPushHandlers() {
      console.log("doRegisterPushHandlers");
      // note that this will implicitly register for push notifications, so there's no need to call 'registerForPushNotifications'
      messaging.addOnPushTokenReceivedCallback(token => {
        // you can use this token to send to your own backend server,
        // so you can send notifications to this specific device
        console.log("Firebase plugin received a push token: " + token);

        this.$store.commit("SET_FCM_TOKEN", token);
      });
      messaging
        .addOnMessageReceivedCallback(message => {
          console.log(
            "Push message received in push-view-model: " +
              JSON.stringify(message)
          );
          messagesService.notification(message.body, message.title);
        })
        .then(
          () => {
            console.log("Added addOnMessageReceivedCallback");
          },
          err => {
            console.log("Failed to add addOnMessageReceivedCallback: " + err);
          }
        );

      this.doGetCurrentPushToken();
    },

    doGetCurrentPushToken() {
      messaging
        .getCurrentPushToken()
        .then(token => {
          console.log(token);
          this.$store.commit("SET_FCM_TOKEN", token);
        })
        .catch(err => console.log("Error in doGetCurrentPushToken: " + err));
    },

    doRegisterForPushNotifications() {
      console.log("doRegisterForPushNotifications");
      applicationSettings.setBoolean("APP_REGISTERED_FOR_NOTIFICATIONS", true);
      messaging
        .registerForPushNotifications({
          onPushTokenReceivedCallback: token => {
            console.log("Firebase plugin received a push token: " + token);
            this.$store.commit("SET_FCM_TOKEN", token);
          },

          onMessageReceivedCallback: message => {
            console.log(
              "Push message received in push-view-model: " +
                JSON.stringify(message)
            );
            messagesService.notification(message.body, message.title);
          },

          // Whether you want this plugin to automatically display the notifications or just notify the callback. Currently used on iOS only. Default true.
          showNotifications: true,

          // Whether you want this plugin to always handle the notifications when the app is in foreground.
          // Currently used on iOS only. Default false.
          // When false, you can still force showing it when the app is in the foreground by adding 'showWhenInForeground' to the notification as mentioned in the readme.
          showNotificationsWhenInForeground: false
        })
        .then(() => console.log("Registered for push"));
    },

    // after logout
    doUnregisterForPushNotifications() {
      messaging.unregisterForPushNotifications().then();
    }
  }
}).$start();

Using [email protected] and [email protected], I was getting exactly the same error that @academici was, with the stack trace ending on the same method calls.

[...]
Error: Cannot assign primitive value to array of objects.
[...]
Frame: function:'javaObjectArray',
Frame: function:'createDummyZeroDurationAnimator',

Next I read the solution proposed by @rubensfernandes and thought "That doesn't look related at all. That can't possibly be the fix." - but I tried it, and sure enough, it worked. It seems that the app crashes in a _completely unrelated_ place if you don't sanitize the nativescript-plugin-firebase message object before you pass it to anywhere that the NativeScript-Vue observables would read from. In my case I'm passing it to a VueX store, but if I were using it in data on a Vue instance, it would fail all the same.

      onMessageReceivedCallback: (message) => {
        // sanitize the message or the NativeScript-Vue app will crash on observing it!
        const messageString = JSON.stringify(message)
        const sanitizedMessage = JSON.parse(messageString)
        console.log('Push message received: ' + messageString)
        store.dispatch('handlePushNotification', sanitizedMessage)
      }

Good catch, @rubensfernandes!

I was able to resolve by putting a JSON.stringify in the message console

onMessageReceivedCallback: message => {
   console.log("Push message received: ");
   console.log(JSON.stringify(message));
},

By clicking on the notification the problem of before disappeared!

馃

the problem has disappeared, but I cannot capture the title and body or other content of the notification. It shows me a couple of data there.

What I need is to capture the data that by tapping, I can capture them

{"foreground":false, "data": {"google.delivered_priority":"high", "google.sent_time":{}, "google.ttl":{}, "google.original_priority":"high", "google.message_id":"0:1568790767182616%c40b45a9c40b45a9"}, "google.delivered_priority":"high", "google.sent_time":{}, "google.ttl":{}, "google.original_priority":"high", "google.message_id":"0:1568790767182616%c40b45a9c40b45a9"}

Was this page helpful?
0 / 5 - 0 ratings