Richou / react-native-android-location-enabler

Display a GoogleMap like android popup to ask for user to enable location services if disabled
MIT License
204 stars 51 forks source link

App crashed when app state changes from background to foreground #13

Closed sujay-bidchat closed 6 years ago

sujay-bidchat commented 6 years ago

App crashed when app state changes from background to foreground. react-native: 0.47 "react-native-android-location-enabler": "1.0.5"

Fatal Exception: java.lang.RuntimeException: Illegal callback invocation from native module. This callback type only permits a single invocation from native code.
       at com.facebook.react.bridge.CallbackImpl.invoke(CallbackImpl.java:30)
       at com.facebook.react.bridge.PromiseImpl.resolve(PromiseImpl.java:32)
       at com.heanoria.library.reactnative.locationenabler.RNAndroidLocationEnablerModule.onResult(RNAndroidLocationEnablerModule.java:108)
       at com.heanoria.library.reactnative.locationenabler.RNAndroidLocationEnablerModule.onResult(RNAndroidLocationEnablerModule.java:37)
       at com.google.android.gms.common.api.internal.BasePendingResult$CallbackHandler.handleMessage(Unknown Source)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6123)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Richou commented 6 years ago

Can you post a piece of code, to be able to reproduce the crash please ?

sujay-bidchat commented 6 years ago

I call the below code:-

RNAndroidLocationEnabler.promptForEnableLocationIfNeeded({ interval: 10000, fastInterval: 5000 })
                .then(data => {
                    console.log(TAG, 'Data =>', data);
                    callback(true);
                })
                .catch(() => callback(false));

This I call when I go from background to foreground.

sujay-bidchat commented 6 years ago

I got the fix to this :) I have put that code in setTimeout function with some delay.

Richou commented 6 years ago

I'm closing this issue, feel free to reopen it, if you need to.

sujay-bidchat commented 6 years ago

Please reopen this issue. The timeout has not fixed the cause.

sujay-bidchat commented 6 years ago

To fix this set promise = null; after you get the result.

Richou commented 6 years ago

I'll try this, asap. Thx.

Richou commented 6 years ago

I really can't reproduce your crash. Can you update some package version in your package.json ?

I used :

  "dependencies": {
    "react": "16.4.1",
    "react-native": "0.56.0",
    "react-native-android-location-enabler": "^1.0.6"
  },
  "devDependencies": {
    "babel-jest": "23.4.0",
    "babel-preset-react-native": "^5",
    "jest": "23.4.0",
    "react-test-renderer": "16.4.1"
  },

And the code, with the call to the bridge :

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  AppState,
} from 'react-native';

import RNAndroidLocationEnabler from 'react-native-android-location-enabler';

export default class App extends Component<{}> {

  state = {
    appState: AppState.currentState
  }

  componentDidMount() {
    AppState.addEventListener('change', this._handleAppStateChange);
  }

  componentWillUnmount() {
    AppState.removeEventListener('change', this._handleAppStateChange);
  }

  _handleAppStateChange = (nextAppState) => {
    if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
      console.log('App has come to the foreground!')
      this.onLocationPressed()
    }
    this.setState({appState: nextAppState});
  }

  onLocationPressed = () => {
    RNAndroidLocationEnabler.promptForEnableLocationIfNeeded({interval: 10000, fastInterval: 5000})
      .then(data => {
        alert(data);
      }).catch(err => {
        alert("Error " + err.message + ", Code : " + err.code);
      });
  }

  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={this.onLocationPressed}><Text>Click me !</Text></TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

The trick to detect when the app comes from background to foreground is : https://facebook.github.io/react-native/docs/appstate.html

sujay-bidchat commented 6 years ago

This is what I've used:-

"dependencies": {
"react": "16.0.0-alpha.12",
"react-native": 0.47,
"react-native-android-location-enabler": "1.0.5"
  },

"devDependencies": {
    "babel-jest": "20.0.3",
    "babel-preset-react-native": "2.1.0",
    "jest": "20.0.4",
    "react-test-renderer": "16.0.0-alpha.12"
  }

And the code, with the call to the bridge is the same.

I don't know how but I was getting repetitive crash on Android. After I added promise = null; in your existing code it got fixed. Sample code after modification :-

@Override
    public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
        final Status status = locationSettingsResult.getStatus();
        switch (status.getStatusCode()) {
            case LocationSettingsStatusCodes.SUCCESS:
                if (promise != null) promise.resolve("already-enabled");
                break;
            case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                try {
                    status.startResolutionForResult(getCurrentActivity(), REQUEST_CHECK_SETTINGS);
                } catch (IntentSender.SendIntentException exception) {
                    Log.e(TAG, "Failed to show dialog", exception);
                    if (promise != null) promise.reject(ERR_FAILED_OPEN_DIALOG_CODE, new RNAndroidLocationEnablerException("Failed to show dialog", exception));
                }
                break;
            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                if (promise != null) promise.reject(ERR_SETTINGS_CHANGE_UNAVAILABLE_CODE, new RNAndroidLocationEnablerException("Settings change unavailable"));
                break;
        }
        // Fix which I added
        promise = null;
    }

    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CHECK_SETTINGS && promise != null) {
            if (resultCode == RESULT_OK ) {
                promise.resolve("enabled");
            } else {
                promise.reject(ERR_USER_DENIED_CODE, new RNAndroidLocationEnablerException("denied"));
            }
           // Fix which I added
            promise = null;
        }
    }
Richou commented 6 years ago

I finally reproduce the crash, and I made the fix you gave, I will release the fix asap. Thank you !

Richou commented 6 years ago

The fix has been release, with the version 1.0.7, can you tell me if that fixed your crash plz ?