文章目录
更新说明
日期 | 更新内容 |
---|---|
2022-05-09 | 添加RN库需要更新时的注意操作 |
2021-08-21 | 更新RN项目编译时的问题 |
2021-08-17 | 编译原生项目gradle报错 |
1. 编译错误问题
1.1. 运行node命令失败
错误信息:
ReactNative:Running ‘[node, -e, console.log(require(‘react-native/cli’).bin);]’ command failed. (Cannot run program “node”)
出现场景:
该错误信息是在原来项目完全正常的情况下,重新打开运行时就报错了,导致了原生的android项目无法正常编译。
但是如果是在RN项目中直接运行命令
yarn android
是可以正常编译和安装程序的
。所以这里可以很明确这个问题并不是真的问题。
解决方案:
以下是查询到网络上的一些说明,作为参考提供:
- github issue:https://github.com/react-native-community/cli/issues/1226
- stackoverflow:https://stackoverflow.com/questions/61922174/react-native-on-android-cannot-run-program-node-error-2-no-such-file-or-dir
这边尝试解决方案如下可以解决此问题(参考自stackoverflow):
I’m on my Mac, and i solve this by close the android studio completely(close the process), and restart it.
-
基于macOs环境,退出当前所有的android项目,确保整个AS的进程完全退出(
注意不要只退出当前的项目,是所有的AS项目都需要退出,整个进程需要关闭
) - 重新启动相关项目,重新编译即可
1.2. 重命名应用模块名称后无法运行
错误信息:
Build file 'xxx/node_modules/react-native-reanimated/android/build.gradle' line: 11
A problem occurred evaluating project ':react-native-reanimated'.
> Project with path ':app' could not be found in project ':react-native-reanimated'.
出现场景:
使用全新的一个应用模块,代替RN项目默认创建的模块运行,在
yarn android
中无法运行起新的应用模块;或者是将原应用模块重命名了,导致编译报错或无法运行起来。此问题根据错误信息也是可以定位到修改的位置
RN运行起android项目时,会通过gradle脚本读取固定模块
:app
下的一些参数配置,所以只有
:app
的模块名称才能被运行起来。可以通过修改
node_module
文件夹下的gradle文件,调整运行的应用模块
解决方案:
-
打开gradle文件:
xxx/node_modules/react-native-reanimated/android/build.gradle
-
修改以下的
:app
模块名称为需要运行的实际应用模块名称
def engine = "jsc"
//这里限制死了必须是app的模块名称,并且该模块中必须有ext.react.enableHermes的字段
if (project(':app').ext.react.enableHermes) {
engine = "hermes"
}
注意:
在更新的版本中,可能已经不使用此方式限制了应用的名称
,首先对以上提到的
enableHermes
的检查,已经替换为了判断包含了插件
com.android.application
时才进行读取该字段并判断,也就是通过插件直接来区分是一个应用还是一个依赖库了
def detectJsRuntime() {
def runtimeType = "jsc"
rootProject.getSubprojects().forEach({project ->
//这里已经不再指定固定的应用模块了
if (project.plugins.hasPlugin("com.android.application")) {
if (project.ext.react.enableHermes) {
runtimeType = "hermes"
}
}
})
return runtimeType
}
但是与此同时,会在另外的地方去指定应用的名称:
abstract class replaceSoTask extends DefaultTask {
//这里是强制指定了应用的模块
public static String appName = ":app"
public static String buildDir = "../../../android/app/build"
public static String fbjniVersion = "0.3.0"
//...
}
1.3. 模块中未配置
enableHermes
的字段
enableHermes
RN会根据配置信息自行使用JSC或者是Hermes的JS引擎,但是默认的RN项目实际上都是使用的JSC的引擎。在RN的官网中有提及需要在项目中配置上
enableHermes
的字段
//app模块中的build.gradle文件
project.ext.react = [
enableHermes: false, // clean and rebuild if changing
]
很明显这里就是使用了JSC的引擎,而且不人为强制修改是不会变的。但是在
node_modules
中的JS引擎项目中,有使用了这个参数信息进行判断。当不存在这个字段时,就会报错。
def engine = "jsc"
//这里限制死了必须是app的模块名称,并且该模块中必须有ext.react.enableHermes的字段
if (project(':app').ext.react.enableHermes) {
engine = "hermes"
}
解决此问题有两个方式,其中一个方式是按官网的要求,在模块的build.gradle中添加上该字段的配置。
方式二是修改此处的脚本,让其不强制依赖此字段即可。
def engine = "jsc"
//如果存在此字段才进行使用,否则不需要做任何修改
if (project(':app').ext.has("react")
&& project(':app').ext.react.has("enableHermes")) {
def enableTag = project(':app').ext.react.enableHermes
if (enableTag != null && enableTag) {
engine = "hermes"
}
}
注意:有部分 RN 使用到的依赖库的 gradle 中是只关心
application
模块,如果是
library
的话,不需要处理这个字段。
def runtimeType = "jsc"
rootProject.getSubprojects().forEach{project ->
//这里是检查了只有 application 插件的模块才会处理
if (project.plugins.hasPlugin("com.android.application")) {
if (project.ext.react.enableHermes) {
runtimeType = "hermes"
}
}
})
1.4. Android原生应用项目路径名称问题
RN默认创建的android应用是是存放于
android
文件夹下的,所以在默认生成的
node_module
中的gradle文件是以该文件夹为准的。当应用所在文件路径不正确时,可能会报以下错误:
FAILURE: Build failed with an exception.
* Where:
Script 'xxx/node_modules/@react-native-community/cli-platform-android/native_modules.gradle' line: 248
* What went wrong:
A problem occurred evaluating script.
> React Native CLI failed to determine Android project configuration. This is likely due to misconfiguration. Config output:
根据提示的信息和错误的位置,可以查找到在文件
xxx/node_modules/@react-native-community/cli-platform-android/native_modules.gradle
中,有以下的代码,在这里是读取了项目中的
android
项目,然后再进行了后续操作,如果不存在
android
的项目,则会报错。
def dependencies = json["dependencies"]
//这里读取的是android路径下的项目,需要调整为实际项目所在的文件夹名称
def project = json["project"]["android"]
if (project == null) {
throw new Exception("React Native CLI failed to determine Android project configuration. This is likely due to misconfiguration. Config output:\n${json.toMapString()}")
}
但是很遗憾在实际操作中,这里的信息读取了json配置后得到的,而json信息是通过运行命令得出来的,无法单纯修改这里的文件夹名称来调整加载的android项目
,所以
将原生项目引用RN项目中的node_modules并使用其依赖,原生项目的存储位置只能是命名为android的文件夹
。
建议将当前项目所有内容复制到新的文件夹下,并将该文件夹重命名为
android
1.5. 依赖版本太低无法加载ReactApplication
报错信息如:
/Users/Lincoln/android/sdk_demo/app/src/main/java/cn/xlink/sdk/demo/ui/module/main/DemoApplication.java:63: 错误: 找不到符号
public ReactNativeHost getReactNativeHost() {
^
符号: 类 ReactNativeHost
位置: 类 DemoApplication
排除掉常规的错误,可以查看依赖库中使用的React Native的版本号。当前依赖是期望版本为0.64.1。但是实际上查看依赖库中的版本号时,可以发现是0.20.1,而在该低版本中是不存在相应的类的。很明显该版本并不是我们需要的版本。尽管依赖库中我们是使用了+号表示版本号:
implementation "com.facebook.react:react-native:+"
但是因为我们是依赖本地的
node_modules
中的文件,该文件夹中只有0.64.1版本,所以是不应该出现更低版本的。这种情况很可能是依赖库的地址错误,我们再次确认一下全局的依赖库,如现该相对地方也是指向本地的
node_modules
。
allprojects {
repositories {
maven {
url("../node_modules/react-native/android")
}
maven {
url("../node_modules/jsc-android/dist")
}
}
}
但是最重要的坑来了,以上的写法不能是无法正确指向本地的node_modules的
。我们必须类似RN原项目中创建时使用的写法才行:
allprojects {
repositories {
mavenLocal()
maven {
//这里必须添加上$rootDir
url("$rootDir/../node_modules/react-native/android")
}
maven {
//这里必须添加上$rootDir
url("$rootDir/../node_modules/jsc-android/dist")
}
}
}
实际上因为当前的项目已经是主项目了,该地址也是当前的地址,与上述的写法并没有太大的差别。这里唯一的可能是运行脚本的地方,
不在当前项目中,所以导致了读取出来的地址是不正确的,而
$rootDir
是能指向一个绝对地址的。这里特别记录一下这个错误信息。
附上一个与之相关的问题链接,但是该问题与上述描述中的问题不是相同的情况,仅作参考:
compile ‘com.facebook.react:react-native:+’ defaults to 0.20.1
2. 运行时问题
2.1. RN页面UI元素突然消失不可见
在实际使用过程中,发现存在一个特殊的情况,RN的页面可以正常加载,UI元素也可以正常显示出来。但是在显示后约5秒后就消失了,该问题在IOS平台上并不会出现,只在Andriod平台上出现,并且根据前端同事的反馈即使回退版本也是存在该问题的。
可以确认在此之前的测试页面是正常的能显示UI元素也不会出现,整个过程中不管是JS引擎还是RN页面或者是原生系统都没有报出任何错误信息,非常感觉到诡异。
在经过确认确实与RN页面无关后,再重新检查原生这边的实现,发现问题是:
承载了ReatRootView的原生容器中,使用了NestedScrollView包裹起来了
,就是因为这个导致页面会在几秒后突然就消失掉了,只要去掉外部包裹的ScrollView即可以解决这个问题。特此记录一下。
2.2. 编译成功后运行到加载RN页面时,无法正确加载页面报错
报错信息可能如下:
java.lang.UnsatisfiedLinkError: dlopen failed: library "libjsc.so" not found
at java.lang.Runtime.load0(Runtime.java:938)
at java.lang.System.load(System.java:1632)
at com.facebook.soloader.SoLoader$1.load(SoLoader.java:400)
at com.facebook.soloader.DirectorySoSource.loadLibraryFrom(DirectorySoSource.java:77)
at com.facebook.soloader.DirectorySoSource.loadLibrary(DirectorySoSource.java:50)
at com.facebook.soloader.ApplicationSoSource.loadLibrary(ApplicationSoSource.java:89)
该信息是比较明确的,就是缺少了
jsc
的so库,该库是JS引擎是必不可少的。这时我们可以通过反编译APK确认在APK的lib中是否存在该so库。直接在AS中打开build文件夹下的编译生成的apk文件即可检查。
如果出现该问题,说明当前我们没有正确引入对JSC的依赖,需要检查两个地方:
2.2.1. 检查依赖库
-
检查在主项目中gradle文件是否有添加JSC的依赖
implementation "org.webkit:android-jsc:+"
-
检查根目录下的build.gradle文件中,是否有提供JSC依赖库检索的URL
maven { // Android JSC is installed from npm url("$rootDir/../node_modules/jsc-android/dist") }
这个地方需要重点检查一下URL地址是不是对的
2.2.2. 手动导入aar
还有另一种解决方案是,直接手动导入JSC的依赖库即可;因为该依赖库是没有其它依赖对象,是单独存在的,所以可以直接导入。找到JSC库的位置是在
$rootDir/../node_modules/jsc-android/dist
,一般根据RN项目是的情况,是在当前根目录的上一目录下,存在
node_modules
文件夹,再通过以下路径查找到对应的JSC库即可。
参考路径:../node_modules/jsc-android/dist/org/webkit/android-jsc/r245459(这是版本号)/android-jsc-版本.aar
将该路径下的JSC文件作为aar直接导入,可以不添加上述的依赖库引用;
注意,根据自己选择不同的JSC,是可能用到不同的JSC库的。除了
android-jsc
还有另一个是
android-jsc-intl
,这个是取决于RN项目中的配置使用了哪个。
一般和默认情况下,都是使用
android-jsc
的
。
该部分信息可以参考:https://stackoverflow.com/questions/56734877/getting-library-libjsc-so-not-found-after-upgrading-react-native-to-0-60-rc2
def useIntlJsc = false
if (useIntlJsc) {
implementation ‘org.webkit:android-jsc-intl:+’
} else {
implementation ‘org.webkit:android-jsc:+’
}
具体使用的JSC引擎依赖需要根据实际情况而定。
2.2.3. RN bundle使用额外的资源文件
3. RN项目更新
对于已打包出来的 RN 依赖库,已经添加到了相应的本地 aar 依赖及打包了相应的模块后,如果 RN 模块需要更新,需要支持 React 层的其它第三方控件使用时,需要添加相应的依赖库或更新。
3.1. 添加依赖库并更新
对于 JS 需要增加依赖或更新版本的,在
package.json
的版本管理文件中,在
dependencies
节点下更新相应的依赖库或版本。
//如更新两个新的JS依赖库
{
"dependencies":{
"react-native-wheel-color-picker": "^1.2.0",
"chroma-js": "^2.4.2"
}
}
在
dependencies
中添加或更新后,需要同步一下 RN 项目的依赖。
-
删除掉
yarn.lock
文件,防止项目的版本被锁定而出现问题 -
使用命令
yarn
同步项目依赖库(必要时可能需要科学上网,请根据实际情况而定) -
以往正常的项目,使用
yarn android
尝试运行是否正常,目的是为了确认添加的 JS 依赖库被正常添加,相应的 native 依赖库也是正常依赖了能运行起应用。