Expo应用在TestFlight中启动即崩溃问题排查求助
Expo应用TestFlight启动崩溃但
--no-dev模式正常的排查方案 问题描述
我的Expo应用在TestFlight中启动后立即崩溃,但使用--no-dev模式运行一切正常。此前应用存在SplashScreen相关的未捕获异常,将SplashScreen.preventAutoHideAsync();移至应用入口类外后,该异常似乎消失。目前已设置ErrorUtils.setGlobalHandler全局处理函数屏蔽所有错误,需要定位问题根源并修复。
崩溃报告
Incident Identifier: 27B3A01B-8E32-4564-8ABA-4B833E901F4B Distributor ID: com.apple.TestFlight Hardware Model: iPhone13,2 Process: XXX [20508] Path: /private/var/containers/Bundle/Application/3CE6E33A-1730-4AF2-B773-744CB3184871/XXX.app/XXX Identifier: com.xxx Version: 1.0.4 (5) AppStoreTools: 15F31e AppVariant: 1:iPhone13,2:17.4 Beta: YES Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Coalition: com.xxx [2937] Date/Time: 2024-07-15 01:46:02.3170 +0200 Launch Time: 2024-07-15 01:46:00.5863 +0200 OS Version: iPhone OS 17.5.1 (21F90) Release Type: User Baseband Version: 4.50.06 Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: SIGNAL 6 Abort trap: 6 Terminating Process: XXX [20508] Triggered by Thread: 19 Last Exception Backtrace: 0 CoreFoundation 0x1a25ecf20 __exceptionPreprocess + 164 (NSException.m:249) 1 libobjc.A.dylib 0x19a49a018 objc_exception_throw + 60 (objc-exception.mm:356) 2 CoreFoundation 0x1a26eb580 -[NSException raise] + 16 (NSException.m:129) 3 XXX 0x1003cc4ec ErrorRecovery.crash() + 2672 (ErrorRecovery.swift:286) 4 XXX 0x1003cb1f4 ErrorRecovery.runNextTask() + 824 (ErrorRecovery.swift:199) 5 XXX 0x1003cc8c4 closure #1 in closure #1 in ErrorRecovery.tryRelaunchFromCache() + 232 (ErrorRecovery.swift:242) 6 XXX 0x1003b910c thunk for @escaping @callee_guaranteed @Sendable () -> () + 28 (<compiler-generated>:0) 7 libdispatch.dylib 0x1aa49013c _dispatch_call_block_and_release + 32 (init.c:1530) 8 libdispatch.dylib 0x1aa491dd4 _dispatch_client_callout + 20 (object.m:576) 9 libdispatch.dylib 0x1aa499400 _dispatch_lane_serial_drain + 748 (queue.c:3900) 10 libdispatch.dylib 0x1aa499f30 _dispatch_lane_invoke + 380 (queue.c:3991) 11 libdispatch.dylib 0x1aa4a4cb4 _dispatch_root_queue_drain_deferred_wlh + 288 (queue.c:6998) 12 libdispatch.dylib 0x1aa4a4528 _dispatch_workloop_worker_thread + 404 (queue.c:6592) 13 libsystem_pthread.dylib 0x1ff443934 _pthread_wqthread + 288 (pthread.c:2696) 14 libsystem_pthread.dylib 0x1ff4400cc start_wqthread + 8 (:-1)
应用入口代码
SplashScreen.preventAutoHideAsync(); export const Index = () => { useKeepAwake() const [fontsLoaded] = useFonts({ 'regular': require('../assets/fonts/Regular.ttf'), 'bold': require('../assets/fonts/Bold.ttf'), 'monospace': require('../assets/fonts/Regular.ttf'), }); useEffect(() => { Appearance.setColorScheme("dark") const myErrorHandler:ErrorHandlerCallback = (e, isFatal) => {} ErrorUtils.setGlobalHandler(myErrorHandler) },[]) const onLayoutRootView = useCallback(async () => { if (fontsLoaded) { await SplashScreen.hideAsync(); } }, [fontsLoaded]); if (!fontsLoaded) { return null; } return ( <View onLayout={onLayoutRootView} style={{ backgroundColor: "black", flex: 1, width: "100%" }}> <Bootstrap/> </View> ); }
排查与修复步骤
1. 解析崩溃报告核心信息
从崩溃栈可见,崩溃由Expo的ErrorRecovery.crash()触发,说明应用启动时出现了未被捕获的致命错误,当前的全局错误处理器未覆盖到原生层或异步任务的错误。
2. 恢复全局错误日志,获取真实错误
空的ErrorUtils.setGlobalHandler会掩盖JS层错误,临时修改它以捕获并输出错误:
const myErrorHandler:ErrorHandlerCallback = (e, isFatal) => { console.error('全局捕获错误:', e, '是否致命:', isFatal); // 若使用日志服务,此处可上报错误 } ErrorUtils.setGlobalHandler(myErrorHandler)
之后通过Xcode将归档包安装到真机,查看控制台的实时错误输出,或在TestFlight中开启日志收集。
3. 检查SplashScreen处理逻辑
即使已移动preventAutoHideAsync,仍需验证:
- 确保该方法在组件渲染前调用,无异步竞态问题
- 给
hideAsync添加错误捕获,避免隐藏失败导致的异常:const onLayoutRootView = useCallback(async () => { if (fontsLoaded) { try { await SplashScreen.hideAsync(); } catch (e) { console.error('隐藏启动屏失败:', e); } } }, [fontsLoaded]);
4. 排查字体加载问题
- 确认
app.json的assetBundlePatterns包含assets/fonts/*,避免打包时遗漏字体文件 - 手动加载字体并捕获错误,验证路径和字体文件有效性:
import { Font } from 'expo-font'; const [fontsLoaded, setFontsLoaded] = useState(false); useEffect(() => { const loadFonts = async () => { try { await Font.loadAsync({ 'regular': require('../assets/fonts/Regular.ttf'), 'bold': require('../assets/fonts/Bold.ttf'), 'monospace': require('../assets/fonts/Regular.ttf'), }); setFontsLoaded(true); } catch (e) { console.error('字体加载失败:', e); } }; loadFonts(); }, []);
5. 检查Bootstrap组件初始化逻辑
Bootstrap组件的异步操作(如API请求、本地存储读取)可能存在未处理的Promise rejection,需为所有异步逻辑添加try/catch,避免触发致命错误。
6. 本地测试归档包
不要直接依赖TestFlight,用expo build:ios -t archive生成归档包,通过Xcode安装到真机,查看控制台日志,能更精准定位崩溃前的错误。
7. 其他建议
- 移除空的全局错误屏蔽,保留错误上报逻辑,便于后续排查
- 验证Expo SDK版本与iOS 17.5.1的兼容性,尝试升级到最新稳定版
- 排查ErrorRecovery重启逻辑:若应用崩溃后重启再次失败,会触发crash,需找到首次崩溃的根源而非禁用该机制
内容的提问来源于stack exchange,提问作者Niclas
相关产品推荐
相关产品推荐

