最近在维护公司的一个 React Native 项目时,遇到了一个比较典型但又非常折磨人的问题。
项目一直运行正常,上午修改了几个页面样式之后,重新执行 npx react-native run-android,结果应用一直启动失败。
控制台不断输出类似下面的错误:
Unable to load script.
Make sure you're either running Metro or that your bundle is packaged correctly.
第一眼看到这个错误,我下意识认为是 Metro 服务没有启动。
于是先执行:
npx react-native start
重新打开 Metro,再运行 Android。
结果问题依旧。
很多 React Native 开发者看到这个错误都会认为就是 Metro 没启动,但实际上,这只是 React Native 最终表现出来的一个结果,它背后的原因可能有很多。
于是开始正式排查。
第一步:确认 Metro 是否正常
首先观察 Metro 控制台。
发现没有任何请求进入,也就是说 App 根本没有连接到 Metro。
继续检查 8081 端口:
adb reverse tcp:8081 tcp:8081
再次启动。
依旧失败。
说明网络通信并不是问题。
第二步:清理缓存
React Native 最大的特点就是缓存非常多。
包括:
Metro Cache
Gradle Cache
npm Cache
Watchman Cache(Mac)
Hermes Cache
很多奇怪的问题其实都是缓存导致。
于是依次执行:
npx react-native start --reset-cache
然后:
cd android
gradlew clean
删除:
node_modules
重新安装:
npm install
整个过程花了二十多分钟。
启动之后……
还是一样。
到这里开始怀疑并不是缓存问题。
第三步:查看 Android Logcat
很多人出现 React Native 错误,只盯着 Metro。
实际上真正有价值的信息往往都在 Android Logcat。
执行:
adb logcat
终于发现了一条关键日志:
java.lang.UnsatisfiedLinkError
继续往下看:
couldn't find DSO to load
看到这里基本可以确定,不是 JS 的问题,而是 Native 层加载失败。
继续定位。
第四步:检查第三方库
最近几天刚升级了几个依赖:
react-native-reanimated
react-native-screens
react-native-gesture-handler
于是开始怀疑是不是版本冲突。
查看 package.json:
发现:
react-native 0.72.x
而:
react-native-reanimated
升级到了最新版本。
查看官方文档之后才发现,新版本要求 React Native 更高版本。
虽然 npm 安装成功,但是 Native ABI 已经不兼容。
这也是为什么 Metro 完全正常,但是 App 无法启动。
最终解决方案
最后采用两个办法。
第一种:
将 Reanimated 降级到官方兼容版本。
重新安装。
重新执行:
cd android
gradlew clean
重新运行。
问题依旧。
继续排查。
后来发现还有一个遗漏。
Android Studio 自动升级了 Android Gradle Plugin。
而项目中的:
compileSdk
targetSdk
gradle
AGP
Kotlin
版本已经不匹配。
最终统一版本之后:
compileSdk = 34
targetSdk = 34
Gradle = 8.x
AGP = 8.x
Kotlin = 1.9.x
再次执行:
./gradlew clean
重新编译。
项目终于正常启动。
整个问题持续了接近四个小时。
这次排障让我总结了几点经验
第一,不要只相信第一条错误信息。
React Native 输出的很多错误只是表象。
比如:
Unable to load script
真正原因可能是:
Native 崩溃
Hermes 初始化失败
So 库加载失败
Gradle 编译异常
如果只围绕 Metro 去排查,很容易陷入死循环。
第二,Android Logcat 比 Metro 更重要。
Metro 更多是 JS 层。
真正涉及:
JNI
Hermes
So
Crash
基本都要看 Logcat。
很多时候真正的错误只出现一两行。
第三,每次升级依赖,都要查看 Compatibility。
React Native 社区更新非常快。
很多库都会注明:
Compatible with RN 0.73+
如果自己的项目还是 0.71 或 0.72,即使能够安装成功,也不代表一定可以运行。
以前我经常直接执行:
npm update
后来发现这是很多问题的源头。
现在升级任何依赖之前,我都会先看官方兼容说明,再决定是否升级。
第四,遇到问题不要一次改很多地方。
这是我认为最重要的一点。
刚开始排查时,我同时:
更新 Gradle
更新 npm
更新 SDK
更新依赖
结果问题越来越复杂。
后来重新回滚,每次只修改一个地方,再重新编译,很快就锁定了真正的问题。
这种"控制变量"的方法虽然慢一点,但效率反而更高。
写在最后
做 React Native 开发几年之后,我越来越觉得,真正提升开发能力的并不是会写多少业务代码,而是遇到问题时是否有清晰的排查思路。
大多数 Bug 并没有想象中复杂,只是错误信息具有一定迷惑性。相比急着搜索答案,我现在更习惯先分析问题发生在哪一层——是 JavaScript、Native、Gradle,还是环境配置。只有先确定问题范围,再逐步缩小排查路径,才能真正提高解决问题的效率。
每解决一次问题,都会积累一份属于自己的经验。时间久了,你会发现,真正宝贵的并不是收藏了多少篇解决方案,而是逐渐形成了一套适合自己的排障思维。这也是我认为技术成长过程中最有价值的一部分。
评论 (0)
暂无评论,来抢沙发吧。