RN问题集合记录

Posted 疯狂小芋头

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RN问题集合记录相关的知识,希望对你有一定的参考价值。

文章目录


更新说明

日期更新内容
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是可以正常编译和安装程序的。所以这里可以很明确这个问题并不是真的问题。

解决方案:
以下是查询到网络上的一些说明,作为参考提供:

  1. github issue:https://github.com/react-native-community/cli/issues/1226
  2. 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.

  1. 基于macOs环境,退出当前所有的android项目,确保整个AS的进程完全退出(注意不要只退出当前的项目,是所有的AS项目都需要退出,整个进程需要关闭
  2. 重新启动相关项目,重新编译即可

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文件,调整运行的应用模块

解决方案:

  1. 打开gradle文件:xxx/node_modules/react-native-reanimated/android/build.gradle
  2. 修改以下的: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的字段

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().forEachproject ->
    //这里是检查了只有 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. 检查依赖库

  1. 检查在主项目中gradle文件是否有添加JSC的依赖

    implementation "org.webkit:android-jsc:+"
    
  2. 检查根目录下的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 项目的依赖。

  1. 删除掉yarn.lock文件,防止项目的版本被锁定而出现问题
  2. 使用命令yarn同步项目依赖库(必要时可能需要科学上网,请根据实际情况而定)
  3. 以往正常的项目,使用yarn android尝试运行是否正常,目的是为了确认添加的 JS 依赖库被正常添加,相应的 native 依赖库也是正常依赖了能运行起应用。

以上是关于RN问题集合记录的主要内容,如果未能解决你的问题,请参考以下文章

RN问题集合记录

RN问题集合记录

RN下ScrollView包裹FlatList/SectionList类的组件,在IOS键盘遮挡问题

RN与系统底层交互

RN与系统底层交互

rn中所需要用到的第三方依赖