[以下问题适用于 RN 6.0 以上版本,其他版本未测试,不定期更新]
方法数大于 65536
cannot fit requested classes in a single dex file
The number of method references in a .dex file cannot exceed 64K
严格来讲,这是 android 的问题,RN 默认设置触发了这个错误,可在 multidex 找到答案,简单说一下
Android 5.0(API级别小于21)以下不支持多个 dex (android 打包后的库文件),而单个 dex 默认情况下最多只能有 65536 个方法,所以 app 的依赖较多的话,就会大于这个数,就出现这个问题。而RN默认为16(android/build.gradle 中的 minSdkVersion),【根据 更新记录,RN 在 0.64 之后不再支持 API 16-20,默认 minSdkVersion 改为 21,所以不再需要做任何处理】,如果必须要兼容低版本 Android,则只能使用较低版本的 RN,解决方法如下
1: 不兼容 5.0 了,修改 minSdkVersion 版本为 21,该方案也许还要等几年 (衰
2: 想办法减少依赖,不现实
3: 做一些配置修改,具体见: multidex
4: 启用R8压缩,在RN中就是设置 android/app/build.gradle
中 enableProguardInReleaseBuilds=true
启用 R8 生成 apk 会特慢,所以建议是 debug 时使用方案1或方案3,release 尝试使用方案4,但考虑到一般情况下,项目都是在做增量,所以哪怕是启用R8优化,总有一天会还是会超过 65536,还是老老实实用方案 3 吧。可以将 android/app/build/outputs
目录下生产的 apk 拖到 这个网站 查看总方法数
方案 3 在官方文档中已经写的很清楚了,这里记录一下,省的以后翻官方文档了。RN 使用了 androidx,如果是没有使用 andoridx 的项目, 会略有不同,请查阅官方文档
- 修改
android/app/build.gradle
android {
defaultConfig {
multiDexEnabled true // 开启 multiDex 支持
}
}
dependencies {
implementation 'androidx.multidex:multidex:2.0.1' // 引入 multiDex 库
}
- 修改
android/app/src/main/[project]/MainApplication.java
// 修改
import android.app.Application;
// 为
import androidx.multidex.MultiDexApplication;
// 修改
public class MainApplication extends Application implements ReactApplication {
// 为
public class MainApplication extends MultiDexApplication implements ReactApplication {
RN 开启 R8 debug 问题
Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found or could not be created
该问题 答案,在 android/app/proguard-rules.pro
中添加
-keep class com.facebook.react.devsupport.** { *; }
-dontwarn com.facebook.react.devsupport.**
启用 R8 优化,你可能碰到的不止这一个问题,尤其是安装插件多的时候,指不定哪个地方就暴露问题了。要特别小心的检查,如果插件明说了需要添加哪些混淆规则,一定要加上
Expiring Daemon because JVM heap space is exhausted
开启R8,在优化混淆的编译过程中需要内存,默认的不够用就出现这个情况了,一个简单的 答案
// 在 android/gradle.properties 添加
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
// 在 android/app/build.gradle 添加
android {
...
dexOptions {
javaMaxHeapSize "3g"
}
...
}
生成 apk 太大
默认配置是生成了一个全平台兼容的 apk,内部包含了 arm/x86 等的 lib,只需配置 android/app/build.gradle
修改后,将为各平台生成不同的 apk,不出意外,包体积大概为原来的 1/4
// 该值 改为 true
def enableSeparateBuildPerCPUArchitecture = true
android {
splits {
abi {
...
// 若仍需要一个全平台兼容apk,这里改为 true 即可
universalApk false
}
}
}
babel 优化
RN 的编译用的 babel,配置文件为项目根目录的 babel.config.js
,使用 react init project
载入的项目都会有这个。这与一般的 babel 项目没什么不同,所以可以利用 babel 插件对 rn 项目进行优化。
提前备注:若配置 babel 插件后未生效,可尝试
watchman watch-del-all
yarn start --reset-cache
一、自定义别名
对于安装好的第三方应用,可以很舒服的 import * from "moudule"
,但对于自己项目的源码,引入路径就成了可能就是这样子
import helper from './../../utils/helper'
我们可以使用 babel 的 module-resolver 为了让自己的代码引入也更加清爽,使用方法比较简单
安装 yarn add babel-plugin-module-resolver
module.exports = {
// 添加以下配置, 在 alias 中配置别名
plugins: [
['module-resolver', {
// 这里根据自己项目用的, 也可能是 .ts | .native | .tsx 等
extensions: [".ios.js", ".android.js", ".js", ".json"],
alias: {
'@pages': './src/pages',
'@res': './src/res',
'@utils': './src/utils',
},
}],
],
};
弄好这个,在项目中就可以 import helper from '@utils/helper'
, 嗯,舒服多了。
二、console 优化
开发中常用 console debug 或 依赖的第三方组件也会有 console,这些在 product 模式下是影响性能的一个因素,并且还会增加最终打包 js bundle 的体积,增加 app 运行时的内存占用。
使用 remove-console 插件在编译时移除 console 语句。
安装 yarn add babel-plugin-transform-remove-console
module.exports = {
// 添加以下配置
plugins: [
"transform-remove-console"
],
};
三、propTypes 优化
具体原因可参见这个 issue,去除 propTypes 可减小代码体积,甚至可以提升性能。目前发现了 babel-plugin-transform-react-remove-prop-types 这个拓展,还没来得及测试。
vscode 配置
- 不使用 vscode 开发 java,感觉有点用但又不够用,还是 android studio 好用,就在插件中找 java 扩展,把他给禁用了,省的不小心点开个 Java 文件他就自动编译,还编译不好
android Studio 配置
- avd 模拟器默认放到系统用户目录了,磁盘不够用了,在 系统环境变量中添加
ANDROID_SDK_HOME
指定一个目录,将会做为 avd 存储目录,原来的目录可以删除掉。
相册访问
官方组件,但新版 rn 已从核心中移除该插件,若仍需要,可手动安装
yarn add @react-native-community/cameraroll
该扩展主要用于保存图片,另外提供了一个无界面读取图片列表 api,若有心,其实可以用 jsx 做一个界面出来,但对于界面问题,其实有更好的选择
yarn add react-native-image-picker
( 详细 安装文档 )
该扩展自带选择图片的UI界面,同时具有拍摄功能,可以用于 上传或拍摄 的应用场景
3,react-native-image-crop-picker
yarn add react-native-image-crop-picker
该扩展与2类似,但在提供了 UI 界面的同时,额外提供了一个 图片裁剪 的功能
-------权限--------
以上扩展需要使用相册/相机/麦克风,所以需要添加权限
android: 修改 android/app/src/main/AndroidManifest.xml
<manifest ...>
<!-- 添加以下权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest >
iOS,修改 ios/[project]/Info.plist
, 添加以下键值对
...
<dict>
....
<key>NSPhotoLibraryUsageDescription</key>
<string>需要您的同意, $(PRODUCT_NAME) 才能访问相册</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>需要您的同意, $(PRODUCT_NAME) 才能保存图片</string>
<key>NSCameraUsageDescription</key>
<string>需要您的同意, $(PRODUCT_NAME) 才能访问相机</string>
<key>NSMicrophoneUsageDescription</key>
<string>需要您的同意, $(PRODUCT_NAME) 才能使用麦克风录制视频</string>
</dict>
PNG 自动优化
android 使用 gradle 编译,默认会优化 png 文件,这本来是个好事,但在使用热更,尤其是增量更新,这反而成了坏事了,因为自动优化会改变文件 hash,导致后续版本无法正确比对文件,生成增量
禁用该功能参见 官方文档,在 app 的 build.gradle 添加
android {
…
buildTypes {
release {
crunchPngs false
}
}
}
js 环境
这个略坑,需要特别注意。
开启 debug 的情况下使用 chrome v8 引擎,实际运行使用的是 js 环境可能就是 JavaScriptCore 引擎,测试没有问题不代表实际没问题,比如 atob
btoa
这对 base64 互转的函数,在 v8 下是 ok 的,但在 rn 的 js 环境下可能是没有的。
为什么说可能,一是因为 JavaScriptCore 也可能升级,二是 android 可以定制引擎,比如 rn 其实自带了一个 hermes 引擎,社区实现的 react-native-v8 引擎。
总之一句话:在使用一些比较新的 js global 对象时,要注意测试关闭 debug 是否仍然可运行。
android 状态栏
假设要做一个全屏的应用,使用沉浸式状态栏效果比较好,此时整个屏幕都可以使用,那么页面高度设置为屏幕高度即可(若顶部有重要内容,还要考虑刘海屏的问题,可能不合适);但沉浸式在 android 5.0 以下不支持,不用额外安装插件,即可差异化实现。
import { Platform, Dimensions, StatusBar, StyleSheet, View} from 'react-native';
const {width:screenWidth, height:screenHeight} = Dimensions.get('window');
const viewHeight = Platform.OS === 'android' && Platform.Version < 21
? screenHeight - StatusBar.currentHeight : screenHeight;
const styles = StyleSheet.create({
wrapper: {
width:screenWidth,
height:viewHeight,
},
});
export default class extends Component {
render() {
return (<View style={styles.wrapper}>
<StatusBar backgroundColor="transparent" translucent/>
</View>)
}
}
android 路径大全
file://
绝对路径,通常为 APP 私有目录 或 系统共用目录
-
content://
/android.resource://
文件符,这类路径背后其实映射了一个 file://
路径,通常为跨 APP 共享文件的路径,比如 APP 读取 相册这个APP 的文件,相册的文件是私有的,直接给路径,不安全,系统层面不让读取,在用户授权的情况下,相册会生成一个 content://
文件路径给其他 APP 使用。
asset://
android/src/main/assets
目录下的文件,RN 默认是没这个目录的,需要的话可以自行创建一个,通常用来存放一些非常规类型的资源文件,比如静态 dat 文件,PDF 获取其他文档文件,一般用的较少,因为此类文件通常会联网获取。
该目录下文件会被打包到 APK 中,不能使用 RN 的热更进行替换。
res://
android/src/main/res
目录下文件,与 asset://
类似,一般存放图片类型文件,会被 android 索引为资源,即 drawable
,但也可以放一些非常规文件到 android/src/main/res/raw
, 此类文件也会被 android 索引.
在原生层面使用这类文件时,通常是直接调用文件名即可,无需前缀、后缀,所以这也就要求该目录下文件不可能重名,另外对于 drawable
,可以根据 android 的规范针对不同分辨率创建多个目录,存放同名文件,运行时会自动选取最优路径。
在 RN 中, 使用 require('./file')
这种方式调用的文件在最终编译后,会被自动打包进 drawable
或 raw
,在 Debug 模式下, require('./file')
在 RN 内部会解析为 http://
,指向运行时的调试地址,可以动态更新;对于图片,可使用 @2x
这种格式对应不同分辨率;
由于使用了 require
, 所以 RN 可以在中间决定指向的根目录,并提供了自定义指向的接口,若未定义,则指向 android/src/main/res/
,所以这种方式的文件可以很方便的进行热更。
require 本地文件
在 js 文件使用 require(filepath)
来引用本地文件,最常用的是 Image 组件,但可能有第三方拓展也需要这么用,比如 webview / video 等;有可能出现 js 错误的问题,这是因为 RN 对引用本地文件做了限制,仅支持某些后缀,具体支持的后缀可参见 这个文件,如果这些后缀不满足要求,可根据该 文档 进行设置。
即在 rn 项目根目录的 metro.config.js 配置 (该配置会完全覆盖 metro 默认值,要全量配置)
module.exports = {
resolver: {
assetExts: [
'png',
'gif',
....
'm3u8',
'dat'
],
},
...
};
中文名崩溃
RN 0.71 Android 命名中文崩溃,参考 问题,该问题出现在 Debug 版本,主要是依赖的 FLIPPER 导致,只需修改 android/gradle.properties
FLIPPER_VERSION=0.145.0