APK瘦身优化

210 阅读7分钟

1. 何为瘦身优化

  • 所谓瘦身即减小App打包后的包体大小,这样使得APP占用手机空间更少,安装时间更短,在应用市场也能相对提高下载转化率...

2. APK包体分析

2.1 分析工具

  • 工具比较多,但通常来说Android studio自带的Analyze APK即可,方法很简单,直接将APK拖入AS面板即可,如下所示即为我拖入自己开发的玩Android.apk(gitee.com/BTPJ_git/Wa… 结果实例 image.png

2.2 包体主要组成架构

  • classes.dex:Class文件被Dex编译后可提供给ART虚拟机所理解的文件格式
  • res:存放编译后的资源文件 例如drawable、layout等
  • lib:存放一些第三方库的so文件,通常有armeabi、armeabi-v7a、arm64-v8a、x86、x86-64这几种
  • resources.arsc:编译后的二进制资源文件
  • MEAT-INFO:存在于已经签名的apk中,包含了apk文件的签名摘要等信息
  • AndroidManifest.xml:Android清单文件
  • kotlin:编译后的kotlin文件

2.3 包体瘦身方向

  • 删除:包括去除一些无用的资源或代码
  • 压缩:包括针对资源或代码的的压缩、用更节省空间的代替譬如webp代替png等
  • 抽离:包括动态加载(插件化)、动态下发(so库)等等,这个方向涉及很多暂时不在本篇文章考虑范畴

3. Lint工具

  • 删除通常涉及到移除一些无用、重复的代码或资源文件,这时可以用带AS自带的Lint代码扫描分析工具,当然它不仅能发现一些无用的资源代码,也会对代码结构优化做出一定的提醒
  • 在AS的菜单栏中,点击Analyze -> Inspect Code即可调出Lint工具 image.png
  • 选择检查范围为整个工程即可对整个工程进行检查 image.png
  • 点击ok后等待分析结果即可,通过具体情况进行修改/删除 image.png
  • 注意:如果代码中通过反射等手段动态获取资源id,这时候也会被lint检查到认为是没有用过的资源所以lint检查出来的资源删除的时候需要谨慎

4. so库的配置

  • 如果项目引用的一些第三方库SDK/NDK中包含了so库文件,默认不配置cpu架构过滤的话会在打包的时候导入所有架构的so库文件,这会明显的加大包体大小,特别是包含的so库比较多的时候,譬如玩Android引用的MMKV以及bugly底层都使用了C/C++实现所以就存在so库文件,下面是不添加过滤打包后的情况 image.png
  • 由上图可发现光so库就占用了1.1M虽然不是很大,但考虑到整个包体也才5.3M,所以比例还是挺大的
  • 配置过滤只需要到app下面得build.gradle中的android->defaultConfig标签内添加即可
android {
    ...
    defaultConfig {
        ...
        ndk {
            // 设置支持的SO库架构,以微信为基准只支持arm64-v8a即可,可以达到缩包的目的
            abiFilters 'arm64-v8a'//, 'armeabi'//, 'armeabi-v7a', 'x86', 'x86_64'
        }
    }
  • so库通常有armeabi、armeabi-v7a、arm64-v8a、x86、x86-64这几种,具体该选择哪几种呢,因为这涉及到很多CPU机型是否可以正常兼容使用的问题,这里可以只使用arm64-v8a即可,微信app基本都能兼容而它也只使用了这一个,所以当不知道怎么选择,直接把微信app拖入查看一下就可以了
  • 以下是配置了过滤后的包体情况,so库直接降到了253k,可见so库的瘦身效果还是不错的 image.png

5. 混淆与资源压缩

5.1 混淆

  • 混淆可以将代码中的方法名、类名、变量等改成一些无意义的名字可一定作用上达到保护代码的意义,也会移除未被使用的类、方法变量等,从而带来压缩代码的作用,使得包体内的classes.dex更小,而且效果非常显著,当然不是所有的代码都能被混淆,这里就涉及到需要在proguard-rules.pro中配置混淆规则而保证不会因混淆导致程序打包后运行出错 -混淆的方法:app目录下的build.gradle中在android->buildTypes->release标签中加入以下即可
release {
    minifyEnabled true // 开启混淆
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}

5.2 资源压缩

  • 开启混淆的同时可继续修改shrinkResources为true来压缩资源(默认是使用R8压缩),将一些无用的资源压缩成只有名字没有内容的文件,这比手动删除更安全,当然资源压缩开启必须需要混淆同步开启,因为混淆之后会把一些无用的类删掉,那么无用的类中引用的资源也就可以放心的压缩了(实测只开启资源压缩不开启混淆,打包会报错)
  • 资源压缩的方法:app目录下的build.gradle中在android->buildTypes->release标签中加入以下即可
release {
    minifyEnabled true // 开启混淆
    shrinkResources true // 启动资源压缩
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
  • 同步开启混淆和资源压缩会大幅度的瘦身
  • 还有一些开源的资源混淆(资源混淆就是将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a)框架,例如开源工具AndResGuard

6. 图片等资源的处理

6.1 图片处理

  • Android开发中,涉及到屏幕的适配问题,有可能会在mdpi、hpdi、xhdpi、xxhdpi以及xxxhdpi中各放一份当前分辨率下的图片,譬如默认情况下会将logo图在mipmap的5个分辨率的文件夹下各放置一份,但有些情况就没必要了,譬如有下面几种情况
  1. 项目中涉及到的大的图片可以只放在xxhdpi上就可以了,这样既不至于小分辨率机器自动在相应分辨率的文件夹下生成更大的图而加大内存消耗(甚至会oom)也不至于大分辨率机器失真
  2. 一些其他的较小的icon图,可以直接做成svg图,然后使用AS自带的转换工具转成xml文件,直接在drawable上点击New->Vector Assets即可将svg图转换成vector标签的图片,可节省大量的空间(注意:大多数icon可直接在iconfont中找到svg格式的图) image.png
  • 当然还有其他处理图片的方式:
  1. 普通的图片格式(png/jpg)可直接使用AS转换为webp格式,方法是直接点击png/jpg格式的图片右键convert to webP即可,或者drawable右键全部转换
  2. 制作.9图使图片不变形不失真避免各分辨率引入多套图
  3. 使用第三方图片压缩工具将图片进行压缩,例如tinypng

6.2 使用Tint着色器

  • ImageView可以使用tint属性达到同样的图而不同的颜色,避免使用多套颜色不同的图片导致包体增大
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:tint="@color/purple_500"
    android:src="@drawable/ic_un_collect" />
  • 使用tint属性可将图片着色为任意颜色而不用管图片本身的颜色,不用每个颜色弄一张图片

6.3 只保留指定资源

  • Android打包默认会将strings中的文本生成各种语言,以达到国际化的目的,譬如下面为打包后的strings文件就包含多国语言 image.png
  • 倘若你APP本身没有针对其他国家的需求,可进行如下设置只保留汉语等
android {
    ...
    defaultConfig {
        ...
        // 只保留指定汉语资源,也可在后面加入指定语言
        resConfigs('zh-rCN')
        ...
    }
    ...
  }
  • 针对玩Android实测,资源压缩文件resources.arsc可减少到接近原来的一半747k->374.7k image.png

7. 总结

  1. 在build.gradle中添加混淆和资源压缩
  2. 加入so库过滤只保留arm64-v8a
  3. 图片处理,只保留一套图放置在drawable-xxhdpi目录中、icon图使用svg图转为vector、png/jpg图转为webp,使用tinypng等第三方工具压缩图片...
  4. 使用Tint着色器避免放置多套不同颜色的图片
  5. 只保留需要的语言资源