本专栏专一分享大型Bat面试知识,后续会持续更新,喜欢的话麻烦点击一个关注html
github面试↓专题连接
关于我
面试官: 类比于微信,如何对Apk进行极限压缩,谈下Android压缩8大步
(面试官是很是注重性能优化的,考求职者是否具有APK压缩技能)node
求职者:应该从每一步压缩开始,压缩的过程本质是挤牙膏的过程,一步一步挤。将Apk压缩分为8个步骤android
随着项目的不断迭代,代码量跟资源文件不断增多。那么就会出现打包后的 APK 文件愈来愈大,若是忽然有一天大家老板或领导叫你优化 APK 大小,你还不知道怎么优化那就有点说不过去了,这篇文章我们就来一块儿分析并优化 APK 体积大小吧。git
注意:
在 GitHub 找了一我的气比较高的开源项目,须要的话本身能够点击下载,本身动手尝试一番.程序员
分析工具直接用的 AS Build/Analyze APKgithub
从上面图中得出 assets > classes.dex > res > lib 其中资源文件占用最大。web
下面咱们就来看看怎么减少 APK 大小吧,面试
WebP 是一种同时提供了有损压缩与无损压缩的图片文件格式,派生自视频编码格式 VP8。WebP 最初在2010年发布,目标是减小文件大小,但达到 和 JEPG 格式相同的图片质量,但愿可以减小图片档在网络上的发送时间。2011年11月8日,Google 开始让 WebP 支持无损压缩和透明色的功能。json
根据 Google 较早的测试,WebP 的无损压缩比网络上找到的 PNG 档少了 45% 的文件大小,即便这些 PNG 档在使用 PNGCRUSH 和 PNGOUT 处理过,WebP 仍是能够减小 28% 的文件大小。就目前而言,Webp 可让图片大小平均减小 70% 。WebP 是将来图片格式的发展趋势。api
点击图片或者文件夹右键选择 Convert to Webp 格式,将 png / jpg 图片压缩为 webp 格式图片
最后咱们只减小了不到 200 kb 左右,有可能项目图片资源原本就没有多大,只是太多小图片致使的。
应用场景及优点
在 app/build.gradle 添加
android{ ... defaultConfig{ ... //只保留英语 resConfigs "en" } }
这里咱们发现减小了大概 200 kb
经过反编译 Android 微信版本 得知,微信也只适配了 armeabi-v7a 架构,那么咱们删掉其它库的支持吧。
android{ ... defaultConfig{ ... ndk { //设置支持的SO库架构 abiFilters "armeabi-v7a" } } }
又优化了差很少 600 kb ,继续。
Lint 是 Android Studio 提供的 代码扫描分析工具,它能够帮助咱们发现代码结构 / 质量问题,同时提供一些解决方案,并且这个过程不须要咱们手写测试用例。代码迭代版本一多,很容易会遗留一些无用的代码、资源文件,咱们可使用 Lint 进行清除。
打开 AS 工具,找到 Analyze > Run Inspection By Name > unused resources
优化
发现咱们 link 大概优化了 700 kb继续。
注意
由于 link 是检查有没有引用来作的判断是否使用了资源,那么若是是这种方式勒,因此在删除的时候必定要谨慎
//动态获取资源 id , 未直接使用 R.xx.xx ,则这个 id 表明的资源会被认为没有使用过(相似不能混淆反射类) int indetifier =getResources().getIdentifier("img_bubble_receive", "drawable", getPackageName()); getResources().getDrawable(indetifier);
优化了大概 1.7M 继续
buildTypes { release { minifyEnabled true shrinkResources = true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { shrinkResources = true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
这个有可能 link 删除了无用资源,因此没有在优化了
普通模式也就是自定义模式
若是您有想要保留或舍弃的特定资源,请在您的项目中建立一个包含 <resources>
标记的 XML 文件,并在 tools:keep
属性中指定每一个要保留的资源,在 tools:discard
属性中指定每一个要舍弃的资源。这两个属性都接受逗号分隔的资源名称列表。您可使用星号字符做为通配符
例如:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*" tools:discard="@layout/unused2" />
将该文件保存在项目资源中,例如,保存在 res/raw/keep.xml
。构建不会将该文件打包到 APK 之中。
指定要舍弃的资源可能看似愚蠢,由于您本可将它们删除,但在使用构建变体时,这样作可能颇有用。例如,若是您明知给定资源表面上会在代码中使用(并所以不会被压缩器移除),但实际不会用于给定构建变体,就能够将全部资源放入公用项目目录,而后为每一个构建变体建立一个不一样的 keep.xml
文件。构建工具也可能没法根据须要正确识别资源,这是由于编译器会添加内联资源 ID,而资源分析器可能不知道真正引用的资源和恰巧具备相同值的代码中的整数值之间的差异。
严格模式
正常状况下,资源压缩器可准确断定系统是否使用了资源。不过,若是您的代码调用 Resources.getIdentifier()
(或您的任何库进行了这一调用 - AppCompat 库会执行该调用),这就表示您的代码将根据动态生成的字符串查询资源名称。当您执行这一调用时,默认状况下资源压缩器会采起防护性行为,将全部具备匹配名称格式的资源标记为可能已使用,没法移除。
例如,如下代码会使全部带 img_
前缀的资源标记为已使用。
String name = String.format("img_%1d", angle + 1); res = getResources().getIdentifier(name, "drawable", getPackageName());
资源压缩器还会浏览代码以及各类 res/raw/
资源中的全部字符串常量,寻找格式相似于 file:///android_res/drawable//ic_plus_anim_016.png
的资源网址。若是它找到与其相似的字符串,或找到其余看似可用来构建与其相似的网址的字符串,则不会将它们移除。
这些是默认状况下启用的安全压缩模式的示例。但您能够停用这一“有备无患”处理方式,并指定资源压缩器只保留其肯定已使用的资源。要执行此操做,请在 keep.xml
文件中将 shrinkMode
设置为 strict
,以下所示:
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:shrinkMode="strict" />
若是您确已启用严格压缩模式,而且代码也引用了包含动态生成字符串的资源(如上所示),则必须利用 tools:keep 属性手动保留这些资源。
AndResGuard 是一个缩小 APK 大小的工具,它的原理相似 Java Proguard ,可是只针对资源。它会将本来冗长的资源路径变短,例如将 res/drawable/wechat 变为 r/d/a。
在以往的开发中,咱们一般只混淆了代码,资源文件却暴露在他人面前,res 文件夹下全部文件名的可读性过强。
AndResGuard 的配置
dependencies { classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.16' }
apply plugin: 'AndResGuard' andResGuard { mappingFile = null use7zip = true useSign = true keepRoot = false compressFilePattern = [ "*.png", "*.jpg", "*.jpeg", "*.gif", "resources.arsc" ] whiteList = [ // your icon "R.drawable.icon", // for fabric "R.string.com.crashlytics.*", // for umeng update "R.string.tb_*", "R.layout.tb_*", "R.drawable.tb_*", "R.drawable.u1*", "R.drawable.u2*", "R.color.tb_*", // umeng share for sina "R.drawable.sina*", // for google-services.json "R.string.google_app_id", "R.string.gcm_defaultSenderId", "R.string.default_web_client_id", "R.string.ga_trackingId", "R.string.firebase_database_url", "R.string.google_api_key", "R.string.google_crash_reporting_api_key", //友盟 "R.string.umeng*", "R.string.UM*", "R.layout.umeng*", "R.drawable.umeng*", "R.id.umeng*", "R.anim.umeng*", "R.color.umeng*", "R.style.*UM*", "R.style.umeng*", //融云 "R.drawable.u*", "R.drawable.rc_*", "R.string.rc_*", "R.layout.rc_*", "R.color.rc_*", "R.id.rc_*", "R.style.rc_*", "R.dimen.rc_*", "R.array.rc_*" ] sevenzip { artifact = 'com.tencent.mm:SevenZip:1.2.10' } }
apply from: 'and_res_guard.gradle'
资源压缩了大概 1M
项目体积越大,资源越多,效果就越明显。
使用 Link 删除资源的话,必定要谨慎,提早作好备份。
我们这里由于项目自己只有 22 M 多,最后优化了 4.5 M 下去。也仍是很不容易的。
github面试↓专题连接
关于我
领取Android高级面试专题
很是但愿和你们一块儿交流 , 共同进步
目前是一名程序员,不只分享 Android开发相关知识,同时还分享技术人成长历程,包括我的总结,职场经验,面试经验等,但愿能让你少走一点弯路。