博客 > 软件开发 > 桌面&移动开发 > React Native
# React Native 常见问题集 ## 提示`SHA-1 is not computed`问题 这个问题比较复杂, 因为`react-native`命令有可能是局部的也有可能是全局的. 最后找到了一个凑合能用的解决方案: - 首先卸载掉全局的`react-native`和`react-native-cli`(新建工程改用`npx`), 在工程文件中安装`react-native-cli`并指定特定的版本号, 如`@2.0.1`. - 接下来每次遇到`SHA-1`问题, 按如下步骤进行就可以修复: 1. 把`start`/`run-android`等等运行中的npm命令全部毙掉 2. 在npm根目录运行`npm i` 3. 直接`run-android`, 即可解决 - 记得每次操作npm依赖后都要重新`run-android`, 不然就会出这个问题. 注意不要在`npm i`之后先运行`start`再运行`run-android`, 这样有可能不起作用. ## 在`run-android`途中, 监听`8081`端口的rn主进程自动崩溃(闪退) ### Debug 步骤 在`run-android`之前先手动`start`, 这样`run-android`会直接使用这个已创建的进程, 然后就可以在控制台看到rn主进程崩溃前的报告, 再见机行事. ### 解决方案 如果是`jetifier`问题, 应该是太久没有运行不带`--no-jetifier`参数的`run-andoird`命令了, 把参数去掉运行一遍就好了 ## Android端APP安装后打开时直接闪退 首先使用adb连接到手机, 查看`logcat`: ```sh adb logcat -v time *:E ``` 这样就可以发现程序报了什么错误导致了闪退, 比如我找到的是`couldn't find DSO to load: libfbjni.so`. 紧接着谷歌一下即可得解: ```sh cd ./android ./gradlew clean ``` ## Android不能使用Image播放gif图片 要给安卓加一些依赖项(见文档: <https://reactnative.cn/docs/image>) ## Andoird调试时可以播放视频而发布后不能 见[下文](#怎样添加和使用原生静态资源). ## 怎样添加和使用原生静态资源 > 其实一般情况下, 直接放在RN项目目录的任意位置. 在需要的地方`require`就可以, 自动解决打包和各种尺寸问题. 手动添加静态资源一般见于特殊情况, 如文件过多, 具有一定动态加载需求, 无法靠`require`全部在编译时加载. > 一个例外情况是mp4等视频文件, 安卓端如果要播放视频, 必须把视频放置在`raw`目录下, `require`是不行的. ### Android - 把文件加入`android/app/src/main/assets/`目录中, 使用uri`asset:/文件路径+文件名+扩展名`进行引用 - 把文件加入`android/app/src/main/res/raw`目录中, 使用uri`raw/文件名`进行引用, 注意不能在`raw`目录下再套目录. **<--视频必须用此方法** - 把png或xml文件放入`android/app/src/main/res/drawable`目录中, 直接使用uri`文件名`进行引用 ### iOS - 放置于`ios/src/assets/`目录中, 直接使用uri`文件名`进行引用 - 放置于xcode中的`***.xcassets`资源目录中, 直接使用uri`文件名`进行引用 - 在xcode新建文件夹, 命名为`***.bundle`, 即可触发iOS的资源打包自动识别. 使用`***.bundle/文件路径+文件名+扩展名`进行引用. 或者也可以建好拖进xcode. - 记得要添加到`target`, 注意`Build Phases`下面也要有相应资源. [参考文档](https://reactnative.cn/docs/images#%E4%BD%BF%E7%94%A8%E6%B7%B7%E5%90%88-app-%E7%9A%84%E5%9B%BE%E7%89%87%E8%B5%84%E6%BA%90) ## 怎样手动接驳原生功能 比如在安卓端接入微信和支付宝支付,而在ios端使用苹果内购。 ### RN层面的抽象 RN通过RCTBridge来接驳原生功能,提供的常量叫`NativeModules`。在原生库代码中声明模块名和暴露的方法,然后RN中就可以用`NativeModules.<模块名>`来引用这个库了,使用`NativeModules.<模块名>.<方法名>`来调用方法。 RN可以根据文件扩展名中添加的系统标识(如`.ios.js`、`.android.js`)来打包不同的代码文件,而`import`不用加`.ios`之类的后缀。比如支付功能可以有两个不同抽象实现`PayTool.ios.js` 和 `PayTool.android.js`,引用时只需要`import PayTool from '../tools/PayTool'`。这实际上就是实现了异构原生功能的同构抽象。这样引用可能会被IDE报错,忽略即可,RN实际运行是正常的,如果加上后缀反而实际运行不了。 同时RN还提供了一种代码内的OS检测方法——`Platform.OS`字段,有`ios`、`android`等取值。 ### Android 每一个安卓原生库至少需要提供以下内容: - 一个继承自`ReactContextBaseJavaModule`的`Module`类。 - 重写`getName()`方法,返回此模块的名称,在JS中`NativeModules.<模块名>`时使用。 - 使用`@ReactMethod`注解标记某方法应该暴露给RN。方法是异步的,若需要返回数据给RN,则要在参数列表最后追加一个`Promise`,和JS里的是一回事。方法的返回值恒为void。resolve时传递的Map、实例等会被自动转为json对象。 - 一个继承自`ReactPackage`的`Package`类。其内包含重写的`createNativeModules`方法,方法的返回值是一个包含一个`Module`类实例的List。 至于Activity之类不是必须的,视业务情况使用。这个`Promise`很灵活,甚至可以写成公开静态成员变量,在别的类或线程里resolve它,比如微信支付就是这么做的。 ### iOS 只用过OC版本的接驳,而且也不是很熟练,大致介绍: - 在`.h`中导入`<React/RCTBridgeModule.h>` - 在`.h`中声明`@interface <模块名> : NSObject <RCTBridgeModule>` - 在`.m`中声明`@interface <模块名> : NSObject <RCTBridgeModule>` - 在`.m`中实现`@implementation <模块名>`,然后调用一次`RCT_EXPORT_MODULE()`,然后里面要导出的方法使用`RCT_EXPORT_METHOD(<方法名>:参数)`这样的形式定义 具体的也不是很清楚,毕竟对苹果开发还不是很熟练,去看RN的官方文档吧。。。