Expo Camera EAS构建启动录像时崩溃问题求助
解决Expo Camera自动录像在EAS构建APK中崩溃/不启动的问题
问题核心
- Expo Go环境下,通过
onCameraReady回调触发自动录像可正常工作 - EAS构建生成APK后,应用崩溃或无法自动启动录像
- 首次启动需手动点击录制按钮才能触发录像
解决方案
1. 完善权限检查与等待逻辑
权限请求是异步操作,原代码未等待权限完全授予就可能触发录像,导致EAS构建环境下权限未就绪引发崩溃。修改权限检查逻辑,确保所有权限通过后再启动录像:
async function permission() { const cameraPerm = await Camera.requestCameraPermissionsAsync(); const micPerm = await Camera.requestMicrophonePermissionsAsync(); const mediaPerm = await MediaLibrary.requestPermissionsAsync(); setcameraStatus(cameraPerm); setmicStatus(micPerm); setmediaStatus(mediaPerm); // 返回权限是否全部通过 return cameraPerm.granted && micPerm.granted && mediaPerm.granted; }
2. 替换onCameraReady触发,改用状态监听启动录像
EAS构建的原生环境中,onCameraReady触发时机可能早于相机ref完全初始化或权限就绪。改用useEffect监听相机ref和权限状态,确保两者都就绪后启动录像:
useEffect(() => { async function initAndRecord() { const allPermissionsGranted = await permission(); setStatusBarTranslucent(true); // 等待相机ref初始化完成 + 权限全部通过 const waitForCamera = setInterval(() => { if (camera && allPermissionsGranted) { clearInterval(waitForCamera); record(); } }, 100); return () => clearInterval(waitForCamera); } initAndRecord(); }, []); // 移除Camera组件的onCameraReady属性 <Camera ref={(ref) => setcamera(ref)} style={{flex: 1, aspectRatio: 9/16}} ratio={'16:9'} type={type} >
3. 修复record函数的异步错误处理
原函数混用await与then,且无错误捕获,崩溃可能源于未处理的异常。修改为统一的async/await写法并添加错误处理:
const record = async () => { if (!camera) return; // 确保相机ref存在 try { setstartRecording(true); const video = await camera.recordAsync(); await MediaLibrary.saveToLibraryAsync(video.uri); } catch (error) { console.error('录像失败:', error); setstartRecording(false); Alert.alert('错误', '无法启动录像,请检查权限设置'); } };
4. 确认EAS构建的权限配置
在app.json中明确声明所需权限,确保EAS构建时正确打包权限配置:
{ "expo": { "android": { "permissions": [ "CAMERA", "RECORD_AUDIO", "WRITE_EXTERNAL_STORAGE", "READ_EXTERNAL_STORAGE" ] } } }
修改后的完整代码
import { Camera, CameraType } from 'expo-camera'; import { setStatusBarTranslucent } from 'expo-status-bar'; import { useEffect, useState } from 'react'; import { Alert, Dimensions, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; import { Fontisto } from '@expo/vector-icons'; import * as MediaLibrary from 'expo-media-library'; export default function App() { const [type, settype] = useState(CameraType.back); const [camera, setcamera] = useState(null); const [cameraStatus, setcameraStatus] = useState({}); const [micStatus, setmicStatus] = useState({}); const [mediaStatus, setmediaStatus] = useState({}); const [startRecording, setstartRecording] = useState(false); const [timer, settimer] = useState(0); async function permission() { const cameraPerm = await Camera.requestCameraPermissionsAsync(); const micPerm = await Camera.requestMicrophonePermissionsAsync(); const mediaPerm = await MediaLibrary.requestPermissionsAsync(); setcameraStatus(cameraPerm); setmicStatus(micPerm); setmediaStatus(mediaPerm); return cameraPerm.granted && micPerm.granted && mediaPerm.granted; } useEffect(() => { async function initAndRecord() { const allPermissionsGranted = await permission(); setStatusBarTranslucent(true); const waitForCamera = setInterval(() => { if (camera && allPermissionsGranted) { clearInterval(waitForCamera); record(); } }, 100); return () => clearInterval(waitForCamera); } initAndRecord(); }, []); useEffect(() => { let interval = null; if (startRecording) { interval = setInterval(() => { settimer(prev => prev + 1); }, 1000); } else { clearInterval(interval); settimer(0); } return () => clearInterval(interval); }, [startRecording]); const record = async () => { if (!camera) return; try { setstartRecording(true); const video = await camera.recordAsync(); await MediaLibrary.saveToLibraryAsync(video.uri); } catch (error) { console.error('录像失败:', error); setstartRecording(false); Alert.alert('错误', '无法启动录像,请检查权限设置'); } }; const toggleCameraType = () => { setstartRecording(false); settype(current => (current === CameraType.back ? CameraType.front : CameraType.back)); }; const stopRecording = () => { if (camera) { camera.stopRecording(); setstartRecording(false); } }; return ( <Camera ref={(ref) => setcamera(ref)} style={{flex: 1, aspectRatio: 9/16}} ratio={'16:9'} type={type} > <View style={{position: 'absolute', flexDirection: 'row', bottom: 0, padding: 48, justifyContent: 'space-between', width: Dimensions.get('window').width}}> <View style={{ width: 61, height: 61, borderWidth: 3, borderColor: 'white', borderRadius: 100, backgroundColor: 'black', alignItems: 'center', justifyContent: 'center' }}> <Text style={{ color: 'white', fontSize: 34, fontWeight: 'bold' }}>{timer}</Text> </View> <TouchableOpacity onPress={startRecording ? stopRecording : record}> <Fontisto name="record" size={64} color={startRecording ? "red" : "white"} /> </TouchableOpacity> <TouchableOpacity onPress={toggleCameraType}> <MaterialIcons name="flip-camera-android" size={64} color="white" /> </TouchableOpacity> </View> </Camera> ); } const styles = StyleSheet.create({ camera: { flex: 1, } });
内容的提问来源于stack exchange,提问作者computer
相关产品推荐
相关产品推荐

