react-native-image-picker: App crash with error: Illegal callback invocation from native module. This callback type only permits a single invocation from native code.

Description

After invoking mutliple times ImagePicker.showImagePicker() my react-native based app crashed with the following error: Illegal callback invocation from native module. This callback type only permits a single invocation from native code. Stack tracke is attached below:

com.facebook.react.bridge.CallbackImpl.invoke CallbackImpl.java:30
com.imagepicker.ResponseHelper.invokeResponse ResponseHelper.java:76
com.imagepicker.ResponseHelper.invokeCustomButton ResponseHelper.java:56
com.imagepicker.ImagePickerModule.invokeCustomButton ImagePickerModule.java:497
com.imagepicker.ImagePickerModule$2.onCustomButton ImagePickerModule.java:193
com.imagepicker.utils.UI$1.onClick UI.java:66
android.support.v7.app.AlertController$AlertParams$3.onItemClick AlertController.java:1045
android.widget.AdapterView.performItemClick AdapterView.java:310
android.widget.AbsListView.performItemClick AbsListView.java:1145
android.widget.AbsListView$PerformClick.run AbsListView.java:306
android.widget.AbsListView$3.run AbsListView.java:3903
android.os.Handler.handleCallback Handler.java:739
android.os.Handler.dispatchMessage Handler.java:95
android.os.Looper.loop Looper.java:148
com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run MessageQueueThreadImpl.java:194
java.lang.Thread.run Thread.java:818

How to repeat issue and example

Click many times on e.g buttom that invokes ImagePicker.showImagePicker()

Solution

Currently no idea how ti fix it

Additional Information

  • React Native version: 0.48.3
  • Platform: Android
  • Development Operating System: Windows
  • Dev tools: Android Studio and Atom

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 15
  • Comments: 18

Most upvoted comments

I can reproduce this by calling showImagePicker twice.

The issue is callback from second call replaced the first one, therefore when both dialog of them have been pressed, it will called twice.

If you put ImagePicker.showImagePicker in onPress and use double tap the button it is possible that ImagePicker.showImagePicker will be called twice.

My workaround is add a flag to stop it being called again until the dialog is dismissed.

@cma00000 thanks for the suggestion.

I ended up using the PermissionsAndroid module to request multiple permissions before invoking ImagePicker.showImagePicker. Here’s a slightly different example from the one @cma00000 listed above if anyone’s interested:


    import { PermissionsAndroid } from 'react-native'

...

    try {
      const grants = await PermissionsAndroid.requestMultiple([
        PermissionsAndroid.PERMISSIONS.CAMERA,
        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
      ])

      const cameraGrant = grants[PermissionsAndroid.PERMISSIONS.CAMERA]
      if (cameraGrant === PermissionsAndroid.RESULTS.GRANTED) {
        console.log('You can use the camera')
      } else {
        console.log('Camera permission denied')
        return false
      }

      const storageGrant = grants[PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE]
      if (storageGrant === PermissionsAndroid.RESULTS.GRANTED) {
        console.log('You can use store photos and videos')
      } else {
        console.log('Storage permission denied')
        return false
      }
    } catch (err) {
      console.warn(err)
      return false
    }

...

   ImagePicker.showImagePicker(options, responseFn)

This callback type only permits a single invocation from native code

This comes when the user has clicked the image picker button in your app multiple times, and the second sheet gives this error and app crashes. My solution was to not call the launchCamera or launchImageLibrary when the picker is already open. Used a ref to track whether picker was open or not.

const isPickerOpen = useRef(false);

const handleCamera = () => {
  if(isPickerOpen.current) return;

  isPickerOpen.current = true;

  launchCamera(options, () => {
    isPickerOpen.current = false;
    // ...
  })
}

I have similar problem. Try to ask PermissionsAndroid before imagePicker (Camera and Write External). Example:

import { PermissionsAndroid } from 'react-native';

async requestCameraPermission() {
  try {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.CAMERA,
      {
        'title': 'Cool Photo App Camera Permission',
        'message': 'Cool Photo App needs access to your camera ' +
                   'so you can take awesome pictures.'
      }
    )
    if (granted === PermissionsAndroid.RESULTS.GRANTED) {
      console.log("You can use the camera")
    } else {
      console.log("Camera permission denied")
    }
  } catch (err) {
    console.warn(err)
  }
}

I also get this issue. Any solution here?

Same here. Any idea ? The thing occurs only the first time we take a picture : the App crashes the first time and from the second, it works. I have these lines in my AndroidManifest : <uses-feature android:name="android.hardware.camera" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.hardware.camera.autofocus" />

A sample of the code : render() { return ( … < TouchableOpacity onPress={this.displayImagePicker.bind(this)} > … );

displayImagePicker() { ImagePicker.showImagePicker(this.state.options, (response) => { … }

@HannanShaikYara More or less but asking permissions before taking picture seems to work better:

async requestCameraPermission() {
        try {
            PermissionsAndroid.requestMultiple([PermissionsAndroid.PERMISSIONS.CAMERA, 
                PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE]).then((result) => {
                    if(result['android.permission.CAMERA'] === PermissionsAndroid.RESULTS.GRANTED 
                        && result['android.permission.READ_EXTERNAL_STORAGE'] === PermissionsAndroid.RESULTS.GRANTED
                    )
                    {
                        this.displayImagePicker();
                    }
              })
        } catch (err) {
            
        }
      }

 displayImagePicker() {
        ImagePicker.showImagePicker(this.state.options, (response) => {
...

<TouchableOpacity onPress=this.requestCameraPermission.bind(this)>
...

Hope this helps