-->

React Native app unresponsive on start if debugger

2020-07-13 08:35发布

问题:

I have a React Native (0.59.8) app that works perfectly fine on simulator, perfectly fine when deployed to device through Xcode, but unresponsive when distributed over TestFlight for about 3 minutes on app start only if the user hasn't logged in.

Here is what happens:

  • I install my app through TestFlight
  • Open the app.
  • There is a 'get started' button on welcome page, that takes the user to the login screen. I tap that button
  • Nothing happens for about 2-3 minutes
  • App goes to the login screen and everything starts working perfectly.

Some observations:

  • This happens only on TestFlight archive builds, it doesn't happen on simulator or when deployed through Xcode > Run.
  • This happens on multiple physical devices (though both a iPhone X, we currently don't have any other devices to test).
  • When app is in that "crashed" state, rotating my phone rotates everything perfectly to landspace orientation. i.e., the app is actually running and responding/layouting correcty to rotations. Though, the tab bar icon layout atht the bottom doesn't change correctly to the landscape/portraint orientation.
  • If the user isn't logged in inside my app, and I kill the app and relaunch, same problem occurs.
  • Once the user is logged in everything works.
  • If I kill the app and relaunch when the user is logged in, I get my homepage that is inside a tab bar and has some tappable views. The scroll view scrolls perfectly and smoothly BUT tab bar buttons and tappable links don't work. When I let it stay on for a few minutes, it crashes with the following log from my iPhone in Console.app:

-

*** Terminating app due to uncaught exception 'RCTFatalException: Unhandled JS Exception: Exception in HostFunction: std::bad_alloc', reason: 'Unhandled JS Exception: Exception in HostFunction: std::bad_alloc, stack:
<unknown>@<null>:<null>
<unknown>@4:3638
value@1033:573
onLayout@1033:1815
y@95:576
P@95:719
E@95:773
M@95:1940
H@95:2699
j@95:2514
<unknown>@95:14003
Ue@95:83341
De@95:13673
We@95:13846
receiveEvent@95:14222
value@28:3311
<unknown>@28:822
value@28:2565
value@28:794
value@<null>:<null>
'
*** First throw call stack:
(0x208f5d27c 0x2081379f8 0x100c2703c 0x100c23574 0x208f64900 0x208e464d0 0x208e47104 0x100c3a6b0 0x100c7a1d4 0x100c79f34 0x20899ca38 0x20899d7d4 0x208978dec 0x20897992c 0x208981e08 0x208b7d114 0x208b7fcd4)
  • I can refresh my home page by swiping down, it starts refreshing (which should take a second or two to fetch data), but it stays in refreshing state forever.
  • At the "hanged" state, my phone gets super hot in a minute or two, so I suspect CPU is at 100% though I don't know how to verify it.
  • I send only weekly builds, and there was no such problem last week, and I've changed hundreds of parts of the project so I'm not sure at what point the problem began, as it doesn't occur in simulator or device testing through Xcode.

My dependencies from package.json:

"dependencies": {
    "@babel/runtime": "^7.4.5",
    "@bankify/react-native-animate-number": "^0.2.1",
    "@react-native-community/async-storage": "^1.4.2",
    "@types/algoliasearch": "^3.30.12",
    "@types/react-native-permissions": "^1.1.1",
    "algoliasearch": "^3.33.0",
    "art": "sebmarkbage/art#19/head",
    "base-64": "^0.1.0",
    "global": "^4.3.2",
    "immutability-helper": "^3.0.1",
    "iso-639-1": "^2.0.5",
    "lerna": "^3.14.1",
    "lodash": "^4.17.11",
    "portable-fetch": "^3.0.0",
    "q": "^1.5.1",
    "querystring": "^0.2.0",
    "react": "16.8.6",
    "react-art": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-native": "^0.59.8",
    "react-native-geolocation-service": "^2.0.1",
    "react-native-gesture-handler": "^1.2.1",
    "react-native-intercom": "^13.1.0",
    "react-native-linear-gradient": "^2.5.4",
    "react-native-loading-spinner-overlay": "^1.0.1",
    "react-native-onesignal": "^3.2.14",
    "react-native-permissions": "^1.1.1",
    "react-native-simple-radio-button": "^2.7.3",
    "react-native-static-safe-area-insets": "^1.3.3",
    "react-native-svg": "^9.5.1",
    "react-native-svg-charts": "^5.2.0",
    "react-native-swipeout": "^2.3.6",
    "react-native-web": "^0.11.4",
    "react-native-webview": "^5.10.0",
    "react-navigation": "^3.11.0",
    "react-redux": "^7.0.3",
    "redux": "^4.0.1",
    "redux-api-middleware": "^3.0.1",
    "redux-persist": "^5.10.0",
    "redux-refresh-token": "^0.1.0",
    "reselect": "latest",
    "superagent": "^5.0.5",
    "url": "^0.11.0"
  },

Did anyone ever have such an issue? What might be the cause? It seems like it's related to some combination of layout and state changes, but I don't know how to debug as it only occurs on archived "release" builds.

UPDATE: I've managed to attach to a debug build on Xcode, some other observations:

  • In performance monitor, the RAM usage keeps getting higher and higher forever.
  • The whole CPU is consumed by the React Javascript thread:

  • When I reload the app by shaking the device, the RAM use persists. i.e. my app is using 1.5GB now, I reload the app, it doesnt reset to some sane value, it continues from 1.5GB and keeps increasing. (it obviously does reset when I kill and reopen the app though)

  • Only the RAM (the one labeled with RAM) use is increasing. JSC use is 0 MB. Here is a screenshot of the performance monitor:

  • When/if things go "normal" after a few minutes, everything is butter smooth with normal RAM usage, with both UI and JS threads in 60fps.

UPDATE 2:

  • When I pause the app in Xcode debugger, the JS thread is always at the same function:

  • At that last part I wasn't debugging JS remotely. When I shook the device and selected Debug JS Remotely, nothing happened (though CPU was still maxed out and RAM was growing larger). I paused the execution and it was stuck here:

  • When I tried to reload it crashed and didn't even respond to shake (the developer menu didn't come up). I killed the app and restarted, and it got stuck at splash screen (not even reaching the welcome screen), again stuck at the same line with previous screenshot.

  • Not sure if related, but I'm getting endless errors like nw_socket_handle_socket_event [C4.1:1] Socket SO_ERROR [61: Connection refused] in the log.

UPDATE 3:

  • I enabled Debug JS Remotely again launched my app next day without changing a line of code, and it started with no issues. I can verify that the healthy running app has none of those issues:

  • I've disabled remote JS debugging and the problem came again. I think this explains why I don't have issue on local testing (since I always debug JS remotely) but have problems. I'm updating the question title from What is causing a few minutes of touch unresponsiveness on React Native app start to React Native app unresponsive on start if debugger is not attached to reflect this issue.

回答1:

After clarifying the problem (that it hangs only in debug builds where debugger is not attached) it was more clear. I've read react native ios application crash if remote debug is not enabled and it turns out that it was exactly the same: logs were causing JS thread to hang. They were routed to a debugger if attached with no issue but when there wasn't a debugger it bloated the console.

I've added babel-plugin-transform-remove-console script to my dev dependencies which only triggers on release mode, and the problem is gone. (It still happens if I do NOT have a debugger attached on a debug build, but I always have one attached while developing anyway)

EDIT: It seems that after upgrading to React Native 0.60 (and also some structural changes) babel-plugin-transform-remove-console stopped working for no apparent reason or error. I've added the following code to my app's main index.js to remove logging from production builds:

if (!__DEV__) {
    global.console.log = () => {}
    global.console.warn = () => {}
    global.console.error = () => {}
}

It works perfectly, with no need for any plugin.