Camera: Unable to take 2nd picture. Fail to connect to camera service error

Error:
ANDROID ONLY
Fail to connect to camera service error. See below for screen shot.

Steps to Reproduce:
Camera installed in 2nd screen in a stack navigator. Stack has 3 screens.

For example: Get ready (1st screen) >> camera (2nd screen) >> result (3rd screen)

The first time you navigate this work flow, it works flawlessly. If you navigate this flow a 2nd time, you get an error.

Alternative Installation attempts (all with the same error):

  • Tried installing as a modal

  • Tried installing as a stand alone sceene in it’s own stack navigator

  • Tried installing as a sceene in tab navigator

  • Tried installing as imported component

  • Tried installing in newly created expo base project

Environment:
Android: 5.01, 7.10 (device and simulator)

“expo”: “^21.0.0”
“react”: “16.0.0-alpha.12”,
“react-native”: “https://github.com/expo/react-native/archive/sdk-21.0.2.tar.gz”,
“react-navigation”: “^1.0.0-beta.13”,
“react-redux”: “^5.0.5”,
“redux”: “^3.7.2”,
“redux-persist”: “^4.10.0”,
“redux-thunk”: “^2.2.0”,

Installation:
We’re using the basic example provided here.
With the “snap” method as well. See code below.

Desired Outcome:
This is a medial app. I want to be able to shoot sequential pictures and videos (up to 5) of wounds without error. I would prefer to accomplish this within expo. I’m trying to avoid ejecting the app to fix this issue.

Any help would be greatly appreciated

Code:

_snap = async () => {
    const { navigate } = this.props.navigation;
    if (this.refs.theCamera) {
    Vibration.vibrate();
    let result = await this.refs.theCamera.takePictureAsync();
    if (!result.cancelled) {
      this.props.setFilePath(result.uri);
      navigate("WoundVarify");
    }
  }
};

render(); {
  return (
    <View style={{ flex: 1, backgroundColor: '#fff' }}>

    { this.state.hasCameraPermission &&
    <Camera
      ref="theCamera"
      style={{ flex: 1 }}
      type={this.state.type}
    >
      <View style={{ flex: 1, backgroundColor: 'transparent' }}>
        <TouchableOpacity
          style={{ flex: 1 }}
          onPress={() => {
            this._snap()
          }}
        >
          <Image source={background} style={styles.backgroundImage}>
            <View style={{ flex: 1, backgroundColor: 'transparent', alignSelf: 'center' }}>
              <Text style={{ fontSize: 20, marginTop: 10, color: 'white' }}>
                CLICK TO TAKE PICTURE
              </Text>
            </View>
          </Image>
        </TouchableOpacity>
      </View>
    </Camera>
    }

  </View>
  );
}

Assets:

Is this on an emulator? I just started testing the camera today as well, and my Genymotion emulator for android was giving me the same error after a bit. I had to restart Genymotion, and turn the camera off and on, before it worked again. I also did an “adb kill-server” in console.

Basically it seemed like camera resource was being used by some previous process, probably from one of my app crashes during testing. Not sure which of my steps above fixed it but it worked for me.

Hope that helps.

I got a similar issue with my project running on ios. I don’t have the same auto navigation couped to my component but symtoms are the same. When I first open the camera I can take multiple pictures. Once I enter another view in my navigation stack and renter the camera module and try to take a photo I got error

Possible Unhandled Promise Rejection (id: 0):
Error: The file “4AC5E016-2F6F-4AF5-A1F2-77580EEC6164.jpg” couldn’t be opened because there is no such file.

The project worked smoothly before adding the navigation. So I guess it has to do with the navigation cycle.

Here is my code

import React from 'react';
import { CameraRoll,Text,StyleSheet, View, TouchableOpacity,Vibration, } from 'react-native';
import { Camera,FileSystem, Permissions, } from 'expo';
import GalleryScreen from './GalleryScreen';

import {MaterialCommunityIcons} from '@expo/vector-icons';
import {Ionicons} from '@expo/vector-icons';
import {Entypo} from '@expo/vector-icons';

export default class CameraExample extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasCameraPermission: null,
      type: Camera.Constants.Type.back,
      flash: 'off',
      zoom: 0,
      autoFocus: 'on',
      depth: 0,
      //type: 'back',
      whiteBalance: 'auto',
      ratio: '16:9',
      ratios: [],
      //photoId: 0,
      showGallery: false,
      photos: [],
      flashIcon:'flash-off'
    };
  };

  async componentWillMount() {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    await this.setState({ hasCameraPermission: status === 'granted' });
  }


  takePicture = async function() {
    if (this.camera) {
      this.camera.takePictureAsync().then(data => {
        let saveResult = CameraRoll.saveToCameraRoll(data);
        console.log(saveResult)
        Vibration.vibrate();
      });
    }
  };


  render() {
    const { hasCameraPermission } = this.state;
    const {navigate} = this.props.navigation
    const { params } = this.props.navigation.state;
    console.log('camera',this.props.navigation)
    if (hasCameraPermission === null) {
      return <View />;
    } else if (hasCameraPermission === false) {
      return <Text>No access to camera</Text>;
    } else {
      return (
        <View style={{ flex: 1 }}>
          <Camera 
            style={{ flex: 0.6}} 
            type={this.state.type} 
            flashMode={this.state.flash}
            ref={ref => {this.camera = ref;}}>
          </Camera>
              <TouchableOpacity
                style={[
                  { flex: 0.2, alignSelf: 'center' },
                ]}
                onPress={this.takePicture.bind(this)}>
                <Entypo name='circle' size={60} style={styles.infosymbol}/>
              </TouchableOpacity>
            </View>

            <View style={{paddingHorizontal:5,flex:0.05,flexDirection:'row',justifyContent:'space-between'}}>
              <TouchableOpacity 
              onPress={() => {navigate('GalleryScreen',{})}}>
                <Text style={globalstyles.smallButton}>Gallery</Text>
              </TouchableOpacity>
              <TouchableOpacity>
                <Text style={globalstyles.smallButton}>Camera</Text>
              </TouchableOpacity>
              <TouchableOpacity
                onPress={() => {navigate('AddItem',{images:[]})}}>
                <Text style={globalstyles.smallButton}>New object</Text>
              </TouchableOpacity>
            </View>
        </View>
      );
    }
  }
}
}

the Stack navigator
import React from "react";
import { StackNavigator } from "react-navigation";

import AddItem from './AddItem';
import GalleryScreen from './GalleryScreen';
import Camera from './Camera';

const CameraStack = StackNavigator({
  AddItem: { screen: AddItem },
  Camera: { screen: Camera },
  GalleryScreen: { screen: GalleryScreen },
  },
  {
    initialRoutName:'AddItem',
    headerMode:'screen',
    navigationOptions:{
      headerTitleStyle: {
        color:'#ffffff',
        fontFamily:'ChalkboardSE-Light',
      },
      headerStyle:{
        backgroundColor:'#FF91B4',
        height:35,
        marginTop:0,
        paddingTop:0,
        justifyContent:'center',
        },
      headerBackTitleStyle:{
        color:'#ffffff',
      },
      headerTintColor: 'white',
      }
  }
  );

export default class CameraTab extends React.Component {
  render() {
    return <CameraStack />;
  }
}

Digged a little deeper. The problem seems to be when navigating using my TouchableOpacity

              <TouchableOpacity
                onPress={() => {navigate('AddItem',{images:[]})}}>
                <Text style={globalstyles.smallButton}>New object</Text>
              </TouchableOpacity>

If I navigate using the standard back button in stack navigator I am able to take some more pics.

I guess that this is the same as stevenmahana originally described.

Still no solution. If anyone has any idea how to navigate and passing all necessary parameters please write some lines.

@stevenmahana @robotron @sijoncfd , the theme i am seeing here is that there is a camera related error in tandem with using navigation. I suspect that the problem is that you cannot mount the Camera twice. Unfortunately there is no fix available for this right now – lmk if changing your code to explicitly avoid mounting the camera twice works?

@quinlanj would you able to provide an example for us as reference ? thanks much appreciate

Some prevision to solve this?
Here i have an example. Try open, close and open modal again.

https://snack.expo.io/Bk90LDJRb

Gif example

1 Like

Got the same error on Android 5.0. If I have a stack navigation A → B, this scenario produces the error:
A → B → A → attempting to go again on B will result in camera service failure.

EXAMPLE: https://expo.io/@nemax/rntest

No problem on Android 6 or IOS.

Hello there! Unfortunately this case is very dependent on navigation API. As some of you noticed - camera is fine when using back button and not when navigating to a screen containing Camera for the second time. Kind of workaround could be using reset instead of navigate but I understand this can not be a desired behavior for your apps. We will soon make the apps not crash but showing some warning/failure-callback instead.

However, this issue will be probably fixed on iOS (SDK 23 hopefully) - I am not sure about Android. Sorry for any inconvenience guys!

when navigating to another screen, you could set the camera to null - essentially unmounting it. Then when navigating back to that screen, pass in some prop that tells your render function to set the null variable back to the camera you want to use. I think that would make it so theres never more than 1 Camera mounted at a time

Just to add to what @aalices said, this does seem to be very dependent on API navigation. For example, I noticed the following issue when using React Navigation, drawer navigator:

  • 3 calls in a row to recordAsync and stopRecording, followed by a single navigate, will crash expo
  • only occurs with drawer navigator, but not stack navigator
  • this is running on device, on an iphone 7

UPDATE:
This doesn’t seem to be depend on navigation at all. If I simply recordAsync and stopRecord 3 times in a row, followed by any attempts to unmount the camera component, it crashes Expo with no message. This is on iPhone 7.

I’m having a similar issue only on Android.

On the same page, I have two upload components with each using the camera component within a modal. Tapping an upload icon in the page will set the modal to be visible and the camera will show up. I can tap the first upload icon to open the camera and take a picture and close it. Note the when closing the camera, I set the reference of the camera as null so nothing should still be referencing the camera service. Then when I tap the second upload icon to try to open the camera again, it threw that Fail to connect to camera service error.

A workaround would be much appreciated!

I think I found a workaround for this error when using react navigation.

Use Tabnavigator and give camera on one tab, and all other screens on second tab. For example:

const mainNavigation = StackNavigator({
     FirstScreen: {
         screen: FirstScreen,
     },
     SecondScreen: {
         screen: SecondScreen,
     },
});

const additionalNavigation = TabNavigator({
     MainTab: {
         screen: mainNavigation,
     },
     CameraTab: {
         screen: cameraScreen
     }
}, {
         navigationOptions: {
             tabBarVisible: false,
         },
         swipeEnabled: false,
});

and when You need camera component You can switch to it by: this.props.navigation.navigate(‘CameraTab’), and when You don’t need it anymore use: this.props.navigation.navigate(‘MainTab’).
This is huge workaround… but it’s working.

Please go vote for this feature request Explicit open/close methods for Camera | Voters | Expo

1 Like

This code solved it for me

import { NavigationEvents } from 'react-navigation';
state = { loaded: true }
render() {
    const { loaded } = this.state;
    return (
      <View style={styles.container}>
        <NavigationEvents
          onWillFocus={payload => this.setState({loaded: true})}
          onDidBlur={payload => this.setState({loaded: false})}/>
      <View style={styles.cameraArea}>
        {loaded && (
          <Camera
            type={Camera.Constants.Type.back}
            ref={ref => {
              this.camera = ref;
            }}
          />
          )}

        </View>

The idea is to hide this camera view (onDidBlur-> loaded: false), then when you come back (onWillFocus is triggered and change loaded to true). When render() function is called it will show the again

https://stackoverflow.com/a/54308457/12029536

1 Like