Cordova app gets two deviceready events, Android crashes on Meteor.settings.public undefined

UPDATE: This issue does turn out to be due to not having defined --mobile-settings on meteor build. That option seems not to be documented anywhere (why not?) except in the meteor build help command line usage text. It explains the Meteor.settings.public being undefined, since --mobile-settings is exactly what defines those settings, as @rjdavid suggested. I had conflated --settings and --mobile-server in my addled mind with the idea of --mobile-settings, and didn’t recognize it as another different command line option. Argh.

As for the two deviceready events, that seems likely to be due to hot code push reloading the app, and generating deviceready from cordova two times. Strangely, I now have another different issue with a vendor cordova plugin, as it blows up on the 2nd deviceready because the plugin is not competely reset by HCP, even though the app is reset enough to generate two deviceready events.

This has been quite painful to unravel, so I’ll leave it here in case it’s helpful to anyone.

END OF UPDATE

I’ve spent a bunch of time trying to figure out what’s going on here, and I’ve just discovered something that I need explained to me.

My cordova app is failing with a white screen the first time it runs after install, but it works every time after that. The error is that ‘public’ is being accessed in an undefined Meteor.settings, but that’s not because of any of the other reasons that Meteor.settings can be undefined. The app works fine in iOS and browser, but as with Android, does send two deviceready events.

Here’s the part that seems really weird. On the first (and ONLY the first) run of the Android app after install, this is the error that kills it:

01-28 18:56:47.865 12761 12761 D SystemWebChromeClient: http://localhost:12072/__cordova/
0c7c3edd55efd812f6df0f23380f7f765d0ae2a9.js?meteor_js_resource=true:
Line 157 : Received Event: deviceready
. . .
01-28 18:56:48.425 12761 12761 I chromium: [INFO:CONSOLE(1)]
"TypeError: Cannot read property 'public' of undefined
01-28 18:56:48.425 12761 12761 I chromium:     at t.e [as render]
(http://localhost:12072/__cordova/
0c7c3edd55efd812f6df0f23380f7f765d0ae2a9.js?meteor_js_resource=true:157:187695)

157:187695 refers to an innocuous Meteor.settings.public definition that is properly defined and works on all other platforms (and on Android after the initial crash). On all subsequent runs, though, the code works properly and doesn’t crash.

Can anyone shed light on this, please? Thanks!

Did you use --mobile-settings during building of the apk?

I have the same problem I believe it is related to HCP issue.
Maybe try with meteor update --release 1.8.1-beta.15?

@rjdavid yep, and the server is accessed on the initial crashing run, and HCP is seen in the logs.

@nauzer I’m not sure how it would be HCP related that it crashes the first time. It could be HCP that causes it to work the 2nd time.

There’s also another odd behavior — I get the deviceready event twice.

I’d really like to know what causes an app to use .js?meteor_js_resource=true rather than app.js?hash=.

Could you try running rm -rf .meteor/local/cordova-build && meteor remove-platform android && meteor add-platform android before running again?

I’d tried that already, per a post I found elsewhere. But here’s my research that proves two deviceready events are being sent, and also that Meteor.settings.public isn’t getting set.

Recall from above that this is only a problem on the FIRST run after install.

Without question, cordova is sending deviceready events twice.

Debugged the android cordova startup like this:

Opened /<meteor_build_dir>/android/project in Android Studio

Determined that the app initially loads /<meteor_build_dir>/android/project/assets/www/application/index.html

In the script in index.html, added a window alert because debugger; doesn’t seem to stop the debug

run
  <script type="text/javascript">
window.alert("get debugger going");
debugger;
    __meteor_runtime_config__ = JSON.parse(...

Chose Run/Debug ‘project’ in the Android Studio menu, selected my USB-connected LG phone as the target

When the window alert popped up on the phone, opened Chrome devtools menu, chose More Tools/Remote Devices

Chose my LG phone in the list of available devices

Chose Inspect for my app that was listed (by name and also by localhost:12072) as running on the phone

Said okay to the window alert

Immediately chose pause in the debug menu

Ended up in cordova.js, so I set a bunch of breakpoints in there related to deviceready

Chose continue in the chrome devtools debug menu

Hit this breakpoint at line 722 of cordova.js for feature ‘onCordovaInfoReady’

    /**
     * Indicate that a feature needs to be initialized before it is ready to be used.
     * This holds up Cordova's "deviceready" event until the feature has been initialized
     * and Cordova.initComplete(feature) is called.
     *
     * @param feature {String}     The unique feature name
     */
    waitForInitialization: function (feature) {
        if (feature) {
            var c = channel[feature] || this.createSticky(feature);

Chose continue

Hit this breakpoint at line 1315 of cordova.js

    // Fire onDeviceReady event once page has fully loaded, all
    // constructors have run and cordova info has been received from native
    // side.
    channel.join(function () {
        require('cordova').fireDocumentEvent('deviceready');

Chose continue

Hit the breakpoint in fireDocumentEvent() that shows the devicready event is fired

Hits a breakpoint in my __cordova/.js file in my deviceready event listener

Hits a breakpoint in my code where I’m checking a value in Meteor.settings.public in my /login page

Boom. White screen and console show crash because Meteor.settings.public isn’t defined

The app now shows my /login react page as present in the Sources, but due to being paused shows no code, and index.html is no longer listed as the file being executed

The app also goes on to show the allow-positioning permission check for a plugin I’m using

I allow the permission

The app continues on (still white screened, but in the debugger) and shows the window alert again with that line shown in the now-visible code of /login, which is loaded by react router as my initial page, and which contains the index.html code copied into it

Chose continue

A-ha! Breakpoint 722 hits again, only this time it’s for feature ‘onCordovaReady’ which didn’t happen before(!), then again for feature ‘onDOMContentLoaded’, then again for ‘onCordovaInfoReady’

Chose continue

The line 1315 breakpoint hits again showing the call to fireDocumentEvent(‘deviceready’)

Another deviceready event fires

This has two big problems:

  1. the __cordova/.js file is not having Meteor.settings.public set
  2. deviceready is sent twice

(note: this post originally also asked why the code runs from __cordova/(hash).js the first time, but from
__cordova/app/app.js after that. the answer to that is the code had been deployed meteor deploy --debug flag, which brought down the app.js source from the server using HCP on the first run and then used that.)

This has been resolved – see the update at the very top of this forum post for the explanations.

2 Likes