RN-Android发行包精简初探
众所周知,随着迭代版本的增加,缩减 React-Native 的 Android 发行包的大小将成为开发人员需要面对的问题之一,究竟如何来做既可以避免引发未知的BUG,又可以有效缩小安装包大小?接下来,我将其分为两个角度分别叙述:
- 在应用开发的前期我们能做什么?
- 木已成舟,面对已经臃肿不堪的发行包如何补救?
在应用开发的前期我们能做什么
1. 缩减图片的使用 & 使用webp
避免在纯色以及渐变的UI部分使用图片,不仅可以缩小安装包体积,还将获得极快的渲染速度,也不用考虑适配问题。
考虑是否使用 webp
格式的资源图片取代 png
图片,当然这仅适用于有大量图片存在的移动端应用程序,同时在服务端开发的过程中也需要进行针对性的优化(例:用户上传后的图片亦需要服务端转码为 webp
格式,缩减请求的资源大小,将有效提升程序的整体响应速度)。但由于 iOS
系统对于 webp
的支持不佳,需要引入第三方包支持。这也造成 iOS
原有的@2x @3x按照设备加载对应图片的机制失效,将影响图片加载效率。
webp
格式图片分为多种,既有 有损压缩
及 无损压缩
,也有动态
、静态
两种模式,应当谨慎选择。
2. 避免使用帧动画
一个简单的帧动画也会包含数十张图片,无论如何压缩,终究会占用大量的空间。因此应当避免使用帧动画,以UI动画来完成,使用 lottie-react-native
将是一个不错的选择。
3. 前期规划
前期的规划极为重要,究竟要使用哪些第三方包?应当及早的做出技术选型,避免中途更换,造成代码冗余。即使因为不可抗力需要更换第三方组件,也应当即时清理原先选用的第三方包带来的冗余依赖及代码。
木已成舟,面对已经臃肿不堪的发行包如何补救
在这一步,我们也应当将其一分为二来看待,分别从 react-native
以及 Android 原生开发
两个角度进行分析。
首先,从我们正在开发的APP进行分析,如下图所示:
lib/:包含特定于处理器软件层的编译代码。该目录包含了每种平台的子目录,如armeabi
,armeabi-v7a
,arm64-v8a
,x86
,x86_64
,和mips
以及 mips64
。
assets/:包含应用可以使用 AssetManager 对象检索的应用资源。
res/:包含未编译及无法编译的资源,主要包含图片、音频资源文件。
classes.dex:包含以 Dalvik/ART 虚拟机可理解的 DEX 文件格式编译类。
kotlin:-
okhttp3:网络请求的开源框架的相关文件。
AndroidManifest.xml:包含核心 Android 清单文件。该文件列出应用程序的名称,版本,访问权限和引用的库文件。其采用 Android 的二进制 XML 格式。
META-INF/:包含 CERT.SF 和 CERT.RSA 签名文件以及 MANIFEST.MF 清单文件。
如果您仔细观察,则会发现存在DownLoad Size
与Raw File Size
两种大小,其中的Raw File Size
是指原始文件的真实大小,即app解压后占用的真实空间。而DownLoad Size
则是指分析工具预估的从 google play 下载本应用所耗费的网络流量大小。
通过上述的分析图可知,lib/
文件夹中的 so
文件占用了最大比重,接近70MB。其次是 assets
目录,占用了28M,第三则是资源文件res目录了(注:res文件夹偏小,是因为所有的图像文件均已经过tinyPNG压缩处理)。
lib/
文件夹精简
作者注:由于网络中技术文章流传的 abi 的各版本描述均已过时,请以本文中对各版本 abi 的描述为准。
我们根据 google-Android 提供的最新文档:
mips
及 mips64
已明确被官方弃用。故此,开发者无需再集成使用。
而上图中未提到的 armeabi
于 ndk-r16版本中被弃用,r17版本中被彻底移除。因此不难得出结论:此版本的 ABI 均应该从我们的应用中移除。
而上图中提到的 x86
以及 x86_64
两种 ABI 一般适用于 老旧的平板电脑、老旧的客厅HTPC、以及部分安卓模拟器(注:截至2020年2月,大部分的安卓模拟器已从软件的方式适配armeabi-v7a),所以,非及其特殊的情况,亦无需集成上述的两种 ABI。
ABIs介绍建议备注armeabi为 ARMv5
和 ARMv6
两代CPU架构提供支持,这些设备目前在消费市场占有率已不足1%无需集成-armeabi - v7a为 ARMv7
架构提供支持,本架构的CPU在消费市场设备留存率中排名第一需集成-arm64 - v8a目前最新的CPU架构支持包,google 官方应用市场已发出通知2019年8月后发布的APP必须集成本 ABI需集成本ABI将有效提高新手机上APP的运行效率x86适用于支持通常称为 x86
、i386
或 IA-32
的指令集的 CPU无需集成-x86_64适用于支持通常称为 x86-64
的指令集的 CPU无需集成-mips官方移除支持无需集成-mips64官方移除支持无需集成-
因此,在通常情况下,我们仅需保留两种ABI:armeabi-v7a
及 arm64-v8a
。 根据此方案,结合现有程序,将节省 38.1MB,减少33.3%的空间占用。
React-native 官方推荐的优化工具:Proguard
Proguard 是官方提供的一个混淆压缩工具,其既可以精简掉 React Native Java(和它的依赖库中)中没有被使用到的部分,也可以混淆代码,提供一定的加密能力。
注意:启用本功能(则无需启用下条提到的shrinkResources方法),可能会导致app出现新的错误,你可在 Proguard 功能所提供的 app/proguard-rules.pro
文件中填写相应的精简排除项,在第三方库的文档中能找到这些配置项。
要启用 Proguard,修改android/app/build.gradle文件:
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
Android 原生开发常用优化工具:shrinkResources
/app/build.gradle
文件中可开启 shrinkResources 功能。当其值为 true 时,表示在编译时自动移除没有引用到的资源文件,主要包括:layout布局文件和drawable图片文件。其处理方式是保留文件,但是内容置空。
注意:本功能的本意是删除冗余,降低编译后的apk包大小。但在某些情况下,这个参数可能导致资源加载失败的问题。如:项目中使用了反射机制来加载图片或布局,如果刚好这些资源文件没有被其他方式引用的话,那么这些资源就会被误判,而被置空。
虽然保留文件,内容置空的方式可避免APP因此崩溃退出,但会直接导致很多令人困惑的错误现象。
打开 /app/build.gradle
文件,需要前置开启 minifyEnabled true
,shrinkResources
依赖其生效,具体如下:
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
//混淆
minifyEnabled true
// 移除无用的resource文件
shrinkResources true
...
}
}
开启后如出现故障,需要添加精简排除对象,可进行如下操作:
- 在
app/src/res/
文件夹中新建raw
文件夹,添加keep.xml
文件:
- 内容包含
<resources>
标记的 XML 文件,示例如下:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:discard="@color/selector_tint_color"
tools:keep="@layout/activity_test1,@layout/activity_test2"
tools:shrinkMode="strict"/>
<!--tools:discard:做严格检查-->
<!--tools:keep:不做严格检查-->
<!--shrinkMode="strict" :该模式只保留在代码或者资源文件中明确引用的资源-->
<!--shrinkMode="safe" :该模式会保留所有明确引用的资源以及可能被 Resources.getIdentifier() 动态引用的资源-->