You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Expo相机因Image Picker权限异常,寻求技术解决方案

React Native Expo ImagePicker 授权后重启应用相机失效问题解决

问题描述

  • 生产环境首次启动应用(无相机权限),点击相机组件触发权限请求后功能正常
  • 授权权限后关闭并重新打开应用,相机功能失效
  • 在系统设置中移除权限后重新打开应用,功能恢复正常

报错信息

Error: Call to function 'ExponentImagePicker.launchCameraAsync' has been rejected.

java.lang.IllegalStateException: Attempting to launch an unregistered ActivityResultLauncher with contract expo.modules.imagepicker.contracts.CameraContract@ebffc59 and input CameraContractOptions(uri=content://com.test.testapp.ImagePickerFileProvider/cached_expo_files/ImagePicker/0f946a22-9efc-4706-8b23-d2d3fc8711f0.jpeg, options=expo.modules.imagepicker.ImagePickerOptions@c77c01e). You must ensure the ActivityResultLauncher is registered before calling launch()] code: 'ERR_UNEXPECTED' }

原因分析

这个错误源于Android平台上Expo ImagePicker的内部机制:ActivityResultLauncher仅在权限请求流程中完成注册。当应用重启且相机权限已处于授权状态时,代码跳过了requestCameraPermissionsAsync步骤,导致launchCameraAsync调用时启动器未完成注册,触发异常。

解决方案

1. 组件挂载时预检查权限(强制初始化启动器)

在组件挂载阶段主动调用getCameraPermissionsAsync,触发ImagePicker内部的启动器注册逻辑,避免仅在点击时才执行权限检查。

2. 升级Expo ImagePicker到最新稳定版

部分旧版本存在该注册逻辑的bug,升级到最新版可解决此类问题:

npx expo install expo-image-picker@latest

3. 确保app.json中配置正确权限

确认app.json的android.permissions包含相机相关权限:

{
  "expo": {
    "android": {
      "permissions": [
        "CAMERA",
        "READ_MEDIA_IMAGES",
        "WRITE_EXTERNAL_STORAGE"
      ]
    }
  }
}

修改后的代码

import React, { useState, useEffect } from "react";
import { View, Text, TouchableOpacity, Image, Alert } from "react-native";
import { styles } from "./styles";
import { Feather } from "@expo/vector-icons";
import * as ImagePicker from "expo-image-picker";
import * as MediaLibrary from "expo-media-library";
import { globalStyles } from "../../../../globalStyles";

const ImageInspect = React.memo(function ImageType({
  data,
  setData,
  title,
}) {
  const [photo, setPhoto] = useState();

  // 组件挂载时预检查权限,初始化ImagePicker启动器
  useEffect(() => {
    const initCameraPermissions = async () => {
      await ImagePicker.getCameraPermissionsAsync();
    };
    initCameraPermissions();
  }, []);

  useEffect(() => {
    if (!!data?.title?.files) {
      setPhoto(data?.title?.files[0]);
    }
  }, [data]);

  const getImageOfCamera = async () => {
    try {
      let permissionResult = await ImagePicker.getCameraPermissionsAsync();
      if (permissionResult.status !== "granted") {
        permissionResult = await ImagePicker.requestCameraPermissionsAsync();
      }
      if (permissionResult.status !== "granted") {
        alert("必须开启相机权限才能使用拍照功能");
        return;
      }

      const result = await ImagePicker.launchCameraAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.Images,
        aspect: [4, 3],
        allowsEditing: false,
        quality: 0.7,
      });

      if (result.canceled) return;

      // 保存到相册,单独捕获错误避免影响主流程
      try {
        await MediaLibrary.saveToLibraryAsync(result.assets[0].uri);
      } catch (saveErr) {
        console.log("保存相册失败:", saveErr);
      }

      setPhoto(result.assets[0].uri);
      setData({
        ...data,
        [data?.value]: {
          title: data?.title,
          files: [result.assets[0].uri],
        },
      });
    } catch (err) {
      console.log("相机调用错误:", err);
      Alert.alert("相机打开失败", JSON.stringify(err));
    }
  };

  return (
    <View style={styles.inputContainer}>
      <Text>{title}</Text>
      <TouchableOpacity onPress={getImageOfCamera}>
        {!!photo ? (
          <View
            style={[globalStyles.marginTopMini, globalStyles.alignItemsCenter]}
          >
            <Image
              source={{ uri: photo }}
              style={{ width: 120, height: 100, borderRadius: 6 }}
            />
          </View>
        ) : (
          <View>
            <Feather name="camera" size={24} />
            <Text> Abrir câmera</Text>
          </View>
        )}
      </TouchableOpacity>
    </View>
  );
});

export default ImageInspect;

关键修改点

  • 添加组件挂载时的initCameraPermissions函数,主动触发权限检查完成ActivityResultLauncher注册
  • 优化错误处理逻辑,避免用户取消拍照时执行后续代码
  • 拆分相册保存的错误捕获,避免单个环节失败导致整个相机流程中断

内容的提问来源于stack exchange,提问作者Victor Ferri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:35:30