viromedia / viro

ViroReact: AR and VR using React Native
MIT License
2.31k stars 483 forks source link

App crashes when leaving a navigator/scene #730

Open antoinerousseau opened 5 years ago

antoinerousseau commented 5 years ago

Environment

Please provide the following information about your environment:

  1. Development OS: macOS 10.14.6
  2. Device OS & Version: Android 9
  3. Version: ViroReact 2.16 and React Native 0.59.10 Note: I'm using react-navigation 3.12
  4. Device(s): Redmi Note 7, Galaxy Tab S5

Description

The app crashes with the following error:

E unknown:ReactNative: Exception in native call
E unknown:ReactNative: java.lang.NullPointerException: Attempt to invoke virtual method 'com.viro.core.Matrix com.viro.core.Node.getWorldTransformRealTime()' on a null object reference
E unknown:ReactNative:  at com.viromedia.bridge.module.NodeModule$4.execute(NodeModule.java:139)
E unknown:ReactNative:  at com.facebook.react.uimanager.UIViewOperationQueue$UIBlockOperation.execute(UIViewOperationQueue.java:583)
E unknown:ReactNative:  at com.facebook.react.uimanager.UIViewOperationQueue$1.run(UIViewOperationQueue.java:917)
E unknown:ReactNative:  at com.facebook.react.uimanager.UIViewOperationQueue.flushPendingBatches(UIViewOperationQueue.java:1025)
E unknown:ReactNative:  at com.facebook.react.uimanager.UIViewOperationQueue.access$2600(UIViewOperationQueue.java:46)
E unknown:ReactNative:  at com.facebook.react.uimanager.UIViewOperationQueue$DispatchUIFrameCallback.doFrameGuarded(UIViewOperationQueue.java:1085)
E unknown:ReactNative:  at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.java:29)
E unknown:ReactNative:  at com.facebook.react.modules.core.ReactChoreographer$ReactChoreographerDispatcher.doFrame(ReactChoreographer.java:166)
E unknown:ReactNative:  at com.facebook.react.modules.core.ChoreographerCompat$FrameCallback$1.doFrame(ChoreographerCompat.java:84)
E unknown:ReactNative:  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1090)
E unknown:ReactNative:  at android.view.Choreographer.doCallbacks(Choreographer.java:888)
E unknown:ReactNative:  at android.view.Choreographer.doFrame(Choreographer.java:816)
E unknown:ReactNative:  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1078)
E unknown:ReactNative:  at android.os.Handler.handleCallback(Handler.java:873)
E unknown:ReactNative:  at android.os.Handler.dispatchMessage(Handler.java:99)
E unknown:ReactNative:  at android.os.Looper.loop(Looper.java:201)
E unknown:ReactNative:  at android.app.ActivityThread.main(ActivityThread.java:6810)
E unknown:ReactNative:  at java.lang.reflect.Method.invoke(Native Method)
E unknown:ReactNative:  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
E unknown:ReactNative:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
E AndroidRuntime: FATAL EXCEPTION: GLThread 33678
E AndroidRuntime: Process: com.artefac, PID: 20162
E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.SurfaceHolder android.opengl.GLSurfaceView.getHolder()' on a null object reference
E AndroidRuntime:   at com.viro.core.ViroViewARCore$ViroARRenderer.onSurfaceCreated(ViroViewARCore.java:262)
E AndroidRuntime:   at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1539)
E AndroidRuntime:   at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1270)

Reproducible Demo

The screen has:

<ViroARSceneNavigator
  apiKey={VIRO_KEY}
  initialScene={{
    scene,
  }}
  autofocus={true}
  style={styles.container}
/>

And the scene is:

import React from 'react'
import { Alert } from 'react-native'
import { connect } from 'react-redux'
import {
  ViroARScene,
  Viro3DObject,
  ViroAmbientLight,
  ViroDirectionalLight,
  ViroNode,
  ViroImage,
} from 'react-viro'

import 'src/Containers/TerreSoleilScene/TerreSoleilAnimations'
import { ANIMATIONS_NAME, CAMERA_VUE_SETTING } from 'src/Config/ConstTerreSoleil'
import TerreSoleilActions from 'src/Stores/TerreSoleil/Actions'

class TerreSoleilScene extends React.Component {
  state = {
    faisceauOnePositionY: 0,
    faisceauTwoPositionY: 0,
    scale: [1, 1, 1],
  }

  render() {
    const {
      displayRayon,
      displayFaisceau,
      displayTrajectoire,

      activatePause,
      activateTerreRotation,
      activateTerreRevolution,

      vitesseTerreRotation,
      vitesseTerreRevolution,

      inclinaisonTerre,
      cameraPosition,
      _onLoadEnd,
      _onLoadStart,
    } = this.props

    return (
      <ViroARScene onTrackingUpdated={this._onTrackingUpdated}>
        <ViroNode // camera position fake -------------------------------
          position={CAMERA_VUE_SETTING.CameraPosition[cameraPosition]}
          scale={this.state.scale} // onPinch
          dragType="FixedToWorld"
          onDrag={() => {}}
          onPinch={this._onPinch}
          ref={this._setARNodeRef}
        >
          <Viro3DObject // soleil -------------------------------
            source={require('src/Assets/TerreSoleil/soleil.glb')}
            position={[0, 0, 0]}
            scale={[0.01, 0.01, 0.01]}
            type="GLB"
            lightReceivingBitMask={4} // soleil mask
            onLoadStart={_onLoadStart}
            onLoadEnd={_onLoadEnd}
            onError={this._onError}
            visible={CAMERA_VUE_SETTING.DisplaySoleil[cameraPosition]}
            animation={{ name: 'animation_soleil', run: true, loop: true }}
          />

          <ViroImage // soleil glow -------------------------------
            height={0.075}
            width={0.075}
            source={require('src/Assets/TerreSoleil/soleil_glow_2.png')}
            visible={CAMERA_VUE_SETTING.DisplaySoleil[cameraPosition]}
            transformBehaviors="billboard"
            lightReceivingBitMask={4}
            renderingOrder={1}
          />

          <Viro3DObject // trajectoire horizontal-------------------------------
            source={require('src/Assets/TerreSoleil/trajectoire.glb')}
            position={[0, 0, 0]}
            scale={[0.01, 0.01, 0.01]}
            lightReceivingBitMask={4}
            opacity={0.7} // soleil
            type="GLB"
            visible={displayTrajectoire}
          />

          <ViroNode // terre_revolution -------------------------------
            position={[0, 0, 0]}
            ref={this._setRevolutionRef}
            onTransformUpdate={this._onTransformUpdate}
            animation={{
              name: ANIMATIONS_NAME.nameAnimationTerreRevolution[vitesseTerreRevolution],
              run: activateTerreRevolution && !activatePause,
              loop: true,
            }}
          >
            <ViroDirectionalLight
              color="#fff8e4"
              intensity={8000}
              direction={[1, 0, 0]}
              castsShadow={true}
              influenceBitMask={3} // terre mask
              shadowNearZ={1}
              shadowFarZ={1}
              shadowOpacity={0.5} // no
            />

            <Viro3DObject // rayon soleil -------------------------------
              source={require('src/Assets/TerreSoleil/rayon.glb')}
              position={[0, 0, 0]}
              scale={[0.01, 0.01, 0.01]}
              type="GLB"
              lightReceivingBitMask={3}
              visible={displayRayon}
            />

            <Viro3DObject // faisceau lumineux haut -------------------------------
              source={require('src/Assets/TerreSoleil/faisceau.glb')}
              position={[0, this.state.faisceauOnePositionY || 0, 0]}
              scale={[0.01, 0.01, 0.01]}
              type="GLB"
              lightReceivingBitMask={4}
              visible={displayFaisceau}
            />

            <Viro3DObject // faisceau lumineux bas -------------------------------
              source={require('src/Assets/TerreSoleil/faisceau.glb')}
              position={[0, this.state.faisceauTwoPositionY || 0, 0]}
              scale={[0.01, 0.01, 0.01]}
              type="GLB"
              lightReceivingBitMask={4}
              visible={displayFaisceau}
            />

            <ViroNode // terre distance soleil -------------------------------
              position={[0.1, 0, 0]}
            >
              <ViroNode //  compensation rotation-------------------------------
                rotation={[0, 0, 0]}
                animation={{
                  name: ANIMATIONS_NAME.nameAnimationTerreRotationParent[vitesseTerreRevolution],
                  run: activateTerreRevolution && !activatePause,
                  loop: true,
                }}
              >
                <Viro3DObject // terre -------------------------------
                  source={require('src/Assets/TerreSoleil/terre.glb')}
                  position={[0, 0, 0]}
                  rotation={[0, 0, inclinaisonTerre]}
                  scale={[0.01, 0.01, 0.01]}
                  type="GLB"
                  lightReceivingBitMask={3} // terre mask
                  animation={{
                    name: ANIMATIONS_NAME.nameAnimationTerreRotation[vitesseTerreRotation],
                    run: activateTerreRotation && !activatePause,
                    loop: true,
                    interruptible: true,
                  }}
                />
              </ViroNode>
            </ViroNode>
          </ViroNode>

          <ViroAmbientLight // terre -------------------------------
            color="#dbebff"
            intensity={150}
            influenceBitMask={3} // terre mask
          />
          <ViroAmbientLight // soleil -------------------------------
            color="#FFFFFF"
            intensity={2500}
            influenceBitMask={4} // soleil mask
          />
        </ViroNode>
      </ViroARScene>
    )
  }

  _onError = (event) => {
    Alert.alert('Viro3DObject error', JSON.stringify(event.nativeEvent.error))
  }

  // -- onScale ----------------------------------------
  _onPinch = (pinchState, scaleFactor, source) => {
    var newScale = this.state.scale.map((x) => {
      return x * scaleFactor
    })

    if (pinchState === 3) {
      this.setState({
        scale: newScale,
      })
      return
    }
    this.arNodeRef.setNativeProps({ scale: newScale })
  }

  _setARNodeRef = (component) => {
    this.arNodeRef = component
  }

  _setRevolutionRef = (component) => {
    this.revolutionRef = component
  }

  updateRevolution = () => {
    // -- get revolution terre euler
    this.revolutionRef.getTransformAsync().then((transform) => {
      const rotation0 = transform.rotation[0]
      const rotation1 = transform.rotation[1]
      const rotation2 = transform.rotation[2]

      let revolutionDegree = 0

      // -- convert degree
      if (
        (rotation0 < 90) &
        (rotation0 > -90) &
        (rotation2 < 90) &
        (rotation2 > -90) &
        (rotation1 > 0)
      ) {
        revolutionDegree = rotation1
      } else if ((rotation0 > 170 || rotation0 < -90) & (rotation2 > 90 || rotation2 < -90)) {
        revolutionDegree = 180 - rotation1
      } else if (
        (rotation0 < 90) &
        (rotation0 > -90) &
        (rotation2 < 90) &
        (rotation2 > -90) &
        (rotation1 < 0)
      ) {
        revolutionDegree = 360 + rotation1
      }
      this._faisceauxPosition(revolutionDegree)
    })
  }

  componentDidMount() {
    this.interval = setInterval(this.updateRevolution, 1000)
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  // -- calcul position des faisceaux avec fonctions d'une parabole
  _faisceauxPosition = (revolutionDegree) => {
    var faisceauOnePositionY
    var faisceauTwoPositionY

    // -- inclinaison terre 23 degree - equation 4 inconnues
    if (this.props.inclinaisonTerre === 23) {
      faisceauOnePositionY =
        (1.3971447441955e-10 * Math.pow(revolutionDegree, 4) -
          1.0059442158207e-7 * Math.pow(revolutionDegree, 3) +
          3.8065843621399e-6 * Math.pow(revolutionDegree, 2) +
          0.00514815 * revolutionDegree +
          0.4) /
        100

      faisceauTwoPositionY =
        (1.0542092160747e-9 * Math.pow(revolutionDegree, 4) -
          7.590306355738e-7 * Math.pow(revolutionDegree, 3) +
          0.000144547 * Math.pow(revolutionDegree, 2) -
          0.00285185 * revolutionDegree -
          0.43) /
        100
    }
    // -- inclinaison terre 45 degree - equation 6 inconnues
    else if (this.props.inclinaisonTerre === 45) {
      faisceauOnePositionY =
        (1.415140141734e-13 * Math.pow(revolutionDegree, 6) -
          1.5283513530731e-10 * Math.pow(revolutionDegree, 5) +
          6.18124656762e-8 * Math.pow(revolutionDegree, 4) -
          0.0000114926 * Math.pow(revolutionDegree, 3) +
          0.000920546 * Math.pow(revolutionDegree, 2) -
          0.0145175 * revolutionDegree) /
        100

      faisceauTwoPositionY =
        (1.8416907991667e-9 * Math.pow(revolutionDegree, 4) -
          1.3260173754e-6 * Math.pow(revolutionDegree, 3) +
          0.000250514 * Math.pow(revolutionDegree, 2) -
          0.00425926 * revolutionDegree -
          0.8) /
        100
    }
    // -- inclinaison terre 0 degree - position fixe
    else {
      faisceauOnePositionY = 0.0077
      faisceauTwoPositionY = 0
    }
    this.setState({
      faisceauOnePositionY: faisceauOnePositionY || 0,
      faisceauTwoPositionY: faisceauTwoPositionY || 0,
    })
  }
}

// get from Reducers
const mapStateToProps = (state) => state.terreSoleil

// send Actions to store
const mapDispatchToProps = (dispatch) => ({
  _onLoadStart: () => dispatch(TerreSoleilActions.setIsLoading(true)),
  _onLoadEnd: () => dispatch(TerreSoleilActions.setIsLoading(false)),
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(TerreSoleilScene)
antoinerousseau commented 5 years ago

@radvani the crash still happens with the freshly released ViroReact v2.17.0, although the release notes say "Fixed potential NPEs when using React Navigation drawer." 😢

FATAL EXCEPTION: GLThread 3471
Process: com.artefac, PID: 28594
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.SurfaceHolder android.opengl.GLSurfaceView.getHolder()' on a null object reference
    at com.viro.core.ViroViewARCore$ViroARRenderer.onSurfaceChanged(ViroViewARCore.java:304)
    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1555)
    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1270)

Related: #620

JuliaRakitina commented 4 years ago

+1 same issue

nhatvuminh commented 3 years ago

+1 same issue

nhatvuminh commented 3 years ago

Good news! I'm trying with createBottomTabNavigator and react-navigation. In TabNavigator, I'm set unmountOnBlur: true, crash is gone. Hope this help! :)

ViktorVojtek commented 1 year ago

This would work: https://github.com/ViroCommunity/viro/issues/138#issuecomment-1336503711