Android App包体积优化

目录:

一、为什么我们需要做 APK 的体积优化?

二、APK 组成

三、APK分析

四、代码体积优化

五、资源体积优化

六、so体积优化

七、其他方案

八、包体积监控

九、总结


一、为什么我们需要做 APK 的体积优化?

1、下载转化率

瘦身优化 最主要的好处是对应用 下载转化率 的影响,包体积越小,用户下载等待的时间也会越短,所以下载转换成功率也就越高。所以,安装包大小与下载转化率的关系 大致是成反比 的,即安装包越大,下载转换率就越小。现在很多大型的 App 一般都会有一个 Lite 版本的 App,这个也是出于下载转化率方面的考虑。

2、渠道合作商的要求

此外,还有一个原因,当我们的 App 做大之后,可能需要跟各个手机厂商合作预装,这些 渠道合作商会对你的 App 做详细的要求,只有达到相应的要求后才允许你的 App 预装到手机上。而且,越大的 App 其单价成本也会越高。所以,瘦身也是我们项目做大之后一定会遇到的一个问题。

3、体积过大对 App 性能的影响

  • 安装时间:安装包越大,安装时间越长
  • 运行时内存:Resource 资源、Library 以及 Dex 类加载都会占用应用的一部分内存。
  • ROM 空间:安装包越大,安装后所占ROM空间越大。如果应用的安装包大小为 50MB,那么启动解压之后很可能就已经超过 100MB 了。

二、APK 组成

Android 项目最终会编译成一个 .apk 后缀的文件,实际上它就是一个 压缩包。因此,它内部还有很多不同类型的文件,这些文件,按照大小,共分为如下四类:

1、代码相关:
classes.dex,我们在项目中所编写的 java 文件,经过编译之后会生成一个 .class 文件,而这些所有的 .class 文件呢,它最终会经过 dx 工具编译生成一个 classes.dex。

2、资源相关:
res、assets、编译后的二进制资源文件 resources.arsc 和 清单文件 等等。res 和 assets 的不同在于 res 目录下的文件会在 .R 文件中生成对应的资源 ID,而 assets 不会自动生成对应的 ID,而是通过 AssetManager 类的接口来获取。此外,每当在 res 文件夹下放一个文件时,aapt 就会自动生成对应的 id 并保存在 .R 文件中,但 .R 文件仅仅只是保证编译程序不会报错,实际上在应用运行时,系统会根据 ID 寻找对应的资源路径,而 resources.arsc 文件就是用来记录这些 ID 和 资源文件位置对应关系 的文件。

3、So 相关:
lib 目录下的文件,这块文件的优化空间其实非常大。

4、META-INF:
它存放了应用的签名信息,不是我们的优化方向,不做具体介绍。

根据apk内的文件组成,我们可以得出我们的三大优化方向:代码(classes.dex)、资源(Assets、res、resources.arsc)、so库。


三、APK分析

1.Android Studio 自带的Analyze APK

Analyze APK 具有如下功能:

  • 可以直观地查看到 APK 的组成,比如大小、占比等等。
  • 查看 dex 文件的组成,可以看到dex声明多少个类,多少个方法。
  • 对不同的 APK 进行对比分析。

2.反编译

  • apktool工具:获取资源文件,提取图片文件,布局文件,还有一些XML的资源文件。
  • dex2jar工具:将APK反编译成Java源码(将classes.dex转化为jar文件)。
  • jd-gui工具:查看dex2jar中转换后的jar文件。

具体请参考:
Android APK反编译教程


四、代码体积优化

1.ProGuard

在 Android SDK 里面集成了一个工具 — Proguard,它是一个免费的 Java 类文件 压缩、优化、混淆、预先校验 的工具。它的主要作用大概可以概括为 两点,如下所示:

  • 瘦身:它可以检测并移除未使用到的类、方法、字段以及指令、冗余代码,并能够对字节码进行深度优化。最后,它还会将类中的字段、方法、类的名称改成简短无意义的名字。
  • 安全:增加代码被反编译的难度,一定程度上保证代码的安全。

使用请参考:
Android 混淆介绍

2.Android Studio Lint 查找无用的代码

ProGuard只是在编译期帮我们删除那些无用代码,在项目里面这些代码还是存在的。但是我们要更好地维护项目的话,一些无用的代码始终还是需要删除的。Lint工具就能够帮我们找到那些无用的代码,包括无用的类、方法和类属性。

如图,Code -> Inspect Code

直接点击确认

搜索结果如图所示,在Java的Unused declaration 和 Kotlin 的 Unused symbol 里面就可以找到那些没有用的类、方法和类属性。不过对于那些被反射调用的类、方法或属性是无法检测出来被调用的,这点需要注意一下。

3.三方库处理

  • 将图片加载库、网络库、数据库以及其他基础库进行统一,去掉冗余的库

  • 同时,在选择第三方 SDK 的时候,我们可以将包大小作为选择的指标之一。在满足我们的需求和性能要求的情况下,我们应该尽可能地选择那些比较小的库来实现相同的功能。例如,对于图片加载功能来说,Picasso、Glide、Fresco 它们都可以实现,但是你引入 Fresco 之后会导致包大小增加很多,而 Picasso 却只增加了不到 100kb,所以引入不同的三方 SDK 对包大小的影响是不一样的。

  • 如果我们引入三方库的时候,可以只引入部分需要的代码,而不是将整个包的代码都引入进来。很多库的代码结构都设计的比较好,比如 Fresco,它将图片加载的各个功能,如 webp、gif 功能进行了剥离,它们都处于单个的库当中。如果我们只需要 Fresco 的 webp 功能,那我们可以将除 webp 之外的别的库都给删掉,这样你引入的三方库就很小了,包大小就降下来了。如果你引入的三方库 没有进行过结构剥离,就需要 修改源码,只提取出来你需要的功能即可


五、资源体积优化

1、冗余资源优化

使用 Lint 的 Remove Unused Resource

APK 的资源主要包括图片、XML,与冗余代码一样,它也可能遗留了很多旧版本当中使用而新版本中不使用的资源,这点在快速开发的 App 中更可能出现。我们可以通过点击右键,选中 Refactor,然后点击 Remove Unused Resource => preview 可以预览找到的无用资源,点击 Do Refactor 可以去除冗余资源。

需要注意的,Android Lint 不会分析 assets 文件夹下的资源,因为 assets 文件可以通过文件名直接访问,不需要通过具体的引用,Lint 无法判断资源是否被用到。

使用 shrinkResources true 来开启资源压缩

资源缩减只有在与代码缩减配合使用时才能发挥作用。在代码缩减器移除所有不使用的代码后,资源缩减器便可确定应用仍要使用的资源,当您添加包含资源的代码库时尤其如此。您必须移除不使用的库代码,使库资源变为未引用资源,因而可由资源缩减器移除。

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

2、移除未使用的备用资源

Gradle 资源缩减器只会移除未由应用代码引用的资源,这意味着,它不会移除用于不同设备配置的备用资源。如有必要,您可以使用 Android Gradle 插件的 resConfigs 属性移除应用不需要的备用资源文件。

以下代码段展示了如何设置只保留英语和法语的语言资源:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

3、图片压缩
对于图片压缩,我们可以在 tinypng 这个网站进行图片压缩,但是如果 App 的图片过多,一个个压缩也是很麻烦的。因此,我们可以 使用 TinyPNG Image Optimizer 插件来对图片进行自动化批量压缩。TinyPNG Image Optimizer 插件一开始使用时需要填写 ApiKey, 申请入口如下 https://tinypng.com/developers

但是,需要注意的是,在 Android 的构建流程中,AAPT 会使用内置的压缩算法来优化 res/drawable/ 目录下的 PNG 图片,但这可能会导致本来已经优化过的图片体积变大,因此,可以通过在 build.gradle 中 设置 cruncherEnabled 来禁止 AAPT 来优化 PNG 图片,代码如下所示:

aaptOptions {
    cruncherEnabled = false
}

4、使用针对性的图片格式
首先,如果能用 VectorDrawable 来表示的话,则优先使用 VectorDrawable;否则,看是否支持 WebP,支持则优先用 WebP;如果也不能使用 WebP,则优先使用 PNG,而 PNG 主要用在展示透明或者简单的图片,对于其它场景可以使用 JPG 格式。简单来说可以归结为如下套路:

VD(纯色icon)->WebP(非纯色icon)->Png(更好效果) ->jpg(若无alpha通道)

注意点:

  • App的minSdkVersion高于14(Android 4.0+)的话,才可以选用WebP格式。
  • 通过Android Studio 的 Vector Asset 可以把矢量图转换成 VectorDrawable,参考 Android Studio神器之Vector Asset
  • 通过Android Studio 的 Conver to Webp 即可把png 转换成 webp格式的图片,使用方式:在Android Studio 中,把图片资源选中,点击鼠标右键即可看到 Conver to Webp的选项。

5、资源混淆t

目前使用较新的Gradle 版本时,发现打release包时资源已经进行了一定程度的混淆。大家可以直接更新到最新版本的Android Studio 版本和 gradle 版本来实现资源混淆。
AndResGuard目前已经很久没维护了,大家可以参考学习其原理。

6、尽量每张图片只保留一份
比如说,我们统一只把图片放到 xhdpi 这个目录下,那么 在不同的分辨率下它会做自动的适配,即 等比例地拉伸或者是缩小。

7、资源在线化
我们可以 将一些图片资源放在服务器,然后 结合图片预加载 的技术手段,这些 既可以满足产品的需要,同时可以减小包大小。


六、so体积优化

1、So 移除方案

So 是 Android 上的动态链接库,在我们 Android 应用开发过程中,有时候 Java 代码不能满足需求,比如一些 加解密算法或者音视频编解码功能,这个时候就必须要通过 C 或者是 C++ 来实现,之后生成 So 文件提供给 Java 层来调用,在生成 So 文件的时候就需要考虑生成市面上不同手机 CPU 架构的文件。目前,Android 一共 支持7种不同类型的 CPU 架构,比如常见的 armeabi、armeabi-v7a、X86 等等。理论上来说,对应架构的 CPU 它的执行效率是最高的,但是这样会导致 在 lib 目录下会多存放了各个平台架构的 So 文件,所以 App 的体积自然也就更大了。
因此,我们就需要对 lib 目录进行缩减,我们 在 build.gradle 中配置这个 abiFiliters 去设置 App 支持的 So 架构,其配置代码如下所示:

defaultConfig {
    ndk {
        abiFilters "armeabi"
    }
}

一般情况下,应用都不需要用到 neon 指令集,我们只需留下 armeabi 目录就可以了。因为 armeabi 目录下的 So 可以兼容别的平台上的 So,相当于是一个万金油,都可以使用。但是,这样 别的平台使用时性能上就会有所损耗,失去了对特定平台的优化。

对于性能敏感的模块,它使用到的 So,我们都放在 armeabi 目录当中随着 Apk 发出去,然后我们在代码中来判断一下当前设备所属的 CPU 类型,根据不同设备 CPU 类型来加载对应架构的 So 文件。这里我们举一个小栗子,比如说我们 armeabi 目录下也加上了 armeabi-v7 对应的 So,然后我们就可以在代码当中做判断,如果你是 armeabi-v7 架构的手机,那我们就直接加载这个 So,以此达到最佳的性能,这样包体积其实也没有增加多少,同时也实现了高性能的目的,比如 微信和腾讯视频 App 里面就使用了这种方式。


七、其他方案

1、插件化

我们可以使用插件化的手段 对代码结构进行调整,如果我们 App 当中的每一个功能都是一个插件,并且都是可以从服务器下发下来的,那 App 的包体积肯定会小很多。

2、业务梳理
我们需要回顾过去的业务,合理地去评估并删除无用或者低价值的业务。简单来说就是删减无用或低价值的功能。

3、转变开发模式
如果所有的功能都不能移除,那就可能需要去转变开发模式,比如可以更多地 采用 H5、小程序 这样开发模式。


八、包体积监控

包体积的监控,主要可以从如下 三个纬度 来进行:

  • 大小监控:通常是记录当前版本与上一个或几个版本直接的变化情况,如果当前版本体积增长较大,则需要分析具体原因,看是否有优化空间。通过Android Studio 自带的Analyze APK就可以看到报体积和各个组成部分的体积大小。
  • 依赖监控:包括Jar、aar 依赖。
  • 规则监控:我们可以把包体积的监控抽象为无用资源、大文件、重复文件、R 文件等这些规则。

包体积的 大小监控 和 依赖监控 都很容易实现,而要实现 规则监控 却得花不少功夫,推荐使用 Matrix 中的 ApkChecker 来实现包体积的规则监控。


九、总结

apk体积的优化方向主要是代码体积优化、资源体积优化和so体积优化,本文总结各部分的一些优化方式,都是比较基本的优化方式,能实现上面提到的点,我们的apk体积优化已经基本ok了。如果需要有需要更加深入的,可以参考下面所列出的本文所参考到的文章。

参考:
缩减、混淆处理和优化应用(官方文档)
Android包体积优化(常规、进阶、极致)
深入探索 Android 包体积优化(匠心制作-上)
深入探索 Android 包体积优化(匠心制作-下)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 为什么要优化包体积 下载转化率:安装包越小,转化率越高; 推广成本:渠道推广成本和厂商预装的单价 应用市场:App...
    今阳说阅读 2,738评论 3 32
  • Android的安装包APK文件本身就是个压缩文件。把后缀名改成.zip,用解压软件解压后,就能看到安装包的内容。...
    wan7451阅读 8,206评论 0 6
  • ### 背景 随着业务快速发展,各种业务功能上线,版本不断迭代,apk体积也越来越大。apk体积过大,一方面会消耗...
    大贝壳seashell阅读 3,873评论 0 6
  • 随着功能的迭代,我们的APP体积也越来越大,因此在保证功能正常的情况下尽可能的降低包体积是一个必须要面对的问题。 ...
    Android小工ing阅读 521评论 0 2
  • 前言: 随着业务的快速迭代增长,App里不断引入新的业务逻辑代码、图片资源和第三方SDK,直接导致APK体积不断增...
    wenxiaohua阅读 125评论 0 0