一、概述
有研究表明当Apk大小超过100M的时候,有20%用户选择取消应用的下载;大小超过100M的Apk被卸载的几率是大小为10M的Apk的8倍。
Apk瘦身的主要作用有两个:
- 提高App的下载率和存留率
- 减少资源检索时间,提高App运行效率
Apk是一种压缩格式,其内部主要组成文件有:res、assets、dex、lib以及AndroidManifest.xml和META-INF等。其中res、assets、dex和lib是主要优化对象。
下面会一一讲解优化方案。
二、Apk包size分析
AndroidStudio提供了专门的Apk包size分析工具——Analyze APK。
通过AndroidStudio->build->Analyze APK...打开,选择要分析的APK文件,如下:
因为项目比较大,远远突破了65535限制,所以配置了multiDexEnabled 为true,dex被拆分为多个文件。
上面Apk的大小已经超过了100M,这里的大小分两种情况:
- APK size 安装后大小,即安装包解压后实际占用的最小磁盘空间大小。
- Download size 下载大小,即从应用商店下载所需的流量大小。
三、Apk包size优化
删除无用资源
res是包size优化大户,通过Analyze APK分析,res已经占到了Apk大小的80%,优化res的最直接方法就是删除无用资源。
点击Analyze -> Inspect Code...,选择要检测的文件,结果如下:
Inspect Code检测项非常多,与Apk瘦身相关的主要有“Unused resources” 和 “Unused code”。
更简单的方法是使用Run Inspect by Name,直接指定检测项。点击Analyze -> Run Inspect by Name...,输入Unused resources。
如果仅仅是删除Unused resources,还有更更简单的方法,直接点击Refactor -> Remove Unused Resources...,所有的无用资源将被清理。
减少打包资源
实际开发中,有些资源即使暂时没有引用,后续可能还会用到,所以不能直接删除,但是打包无用资源又会造成Apk增大。
能否即保留资源,又不打包进Apk呢?答案是肯定的,只需要在build中配置相关项即可。
(1)shrinkResources
通过配置shrinkResources,可以不打包未使用的资源。
buildTypes { release { multiDexEnabled true shrinkResources true } }
注意事项:shrinkResources不是彻底删除无用资源,而是保留文件名,但是没有内容。需要注意的是,如果某个资源只被反射引用(没有直接引用),该资源也会被认为是无用资源,不会保留内容。这样在运行时就会出错甚至崩溃。
(2)resConfigs
通过配置resConfig,可以指定业务需要的语言资源,去除无用语言资源
defaultConfig { applicationId "org.xlgzs.app" minSdkVersion 28 targetSdkVersion 30 versionCode 1 versionName '1.0.0' resConfigs 'zh-rCN','zh-rHK','zh-rTW' }
混淆
混淆可以简化资源路径,比如将res/drawable/activity_settings_lock_icon变为r/d/a,同时让Apk包size减小。
(1)代码混淆
buildTypes { release { multiDexEnabled true minifyEnabled true //代码混淆 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' //混淆文件 } }
(2)资源混淆(AndResGuard)
AndResGuard不涉及编译过程,只需输入一个apk,就可以得到一个资源混淆的apk。同时,通过配置签名信息,可自动重新签名并对齐。
首先在build.gradle中引用插件:
apply plugin: 'com.android.application' apply plugin: 'AndResGuard' //应用AndResGuard插件
buildscript { repositories { jcenter() } dependencies { classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.18' //添加AndResGuard依赖 } }
在build.gradle(app)中配置AndResGuard:
//-----------------------------配置 AndResGuard----------------------------- andResGuard { mappingFile = null // = file("./resource_mapping.txt") //用于keep住资源的路径的mapping文件所在路径 use7zip = true //启用压缩。为true时,useSign必须为true useSign = true // 启用签名。为true时,需要配置signConfig keepRoot = false // 为true时,会keep住所有资源的原始路径,只混淆资源的名字 whiteList = [ //白名单,使用getIdentifier访问的资源需要加入白名单,支持通配符,【+】代表1个或多个,【?】代表0个或1个,【*】代表0个或多个 "R.mipmap.ic_launcher", "R.drawable.icon",// for your icon "R.string.com.crashlytics.*",// for fabric,详见https://docs.fabric.io/android/crashlytics/build-tools.html "R.string.google_app_id",// for google-services "R.id.*",//任意id ] compressFilePattern = [ //需要压缩的文件的匹配规则,一般这里不需要动。支持 ? + * 通配符 "*.png", "*.jpg", "*.jpeg", "*.gif", "resources.arsc" //如果不是对APK size有极致要求,不建议压缩此文件 ] sevenzip { //配置7Zip,只需设置 artifact 或 path;支持同时设置,但此时以 path 的值为优先 artifact = 'com.tencent.mm:SevenZip:1.2.18' //path = "/usr/local/bin/7za" //path指本地安装的7za(7zip命令行工具) } // finalApkBackupPath = "${project.rootDir}/final.apk" //可选,指定生成的apk的保存路径 // digestalg = "SHA-256" //可选: 指定v1签名时生成jar文件的摘要算法,默认值为“SHA-1” }
编译完后在右侧gradle菜单中多了resguradDebug等task,双击执行即可得到资源混淆的Apk。
资源压缩
资源压缩也可以对Apk瘦身起到立竿见影的效果。包括图片压缩,字体压缩,音视频压缩等。
(1)图片压缩
- 使用TinyPNG等工具压缩图片大小
- 纯色类Icon使用SVG
- 两种以上颜色使用WebP,达不到效果再使用PNG
- 无alpha通道考虑使用JPG代替PNG
(2)音视频压缩
- 以有损格式代替无损格式
- 推荐使用ogg
(3)字体压缩
利用FontZip等工具对字体文件优化,删除无用字体。