最近几周一直在研究如何为APK瘦身,折腾了好久,是时候写篇博客总结一下了,虽然已经准备了下周一要在客户端周会分享用的PPT:APK瘦身探索。java
虽说APK瘦身对于Android对应用可分配内存的限制影响不大,可是仍是有一些影响的,就以图片
为例,将一些小图标替换为iconfont能有效减少内存的分配,防止OOM的出现。android
另外,不管是iOS开发者仍是Android开发者都应该尝试最好学会如何为IPA或APK瘦身,不只仅是为了帮助用户省流量、减小下载时间、减小占用的存储空间等等,更重要的是为了提升转化率(注意:本文的转化率均指下载转化率)。git
那转化率是什么呢?github
举个栗子:你的应用大小是 18MB ,有100个潜在用户想要去下载尝试使用,结果有20个用户嫌弃安装包太大直接扬长而去,有20个用户在等待下载的过程当中取消下载,最终只有60个用户真正下载安装,那么应用的转化率就是 60/100 = 60% 。web
那为何要提升转化率呢?api
由于用户在纠结下载你的产品仍是你的竞品的时候,每每会选择那个体验最好、功能最多、性能最好、包最小的。性能优化
如何有条理的为APK瘦身,必须知道APK的目录结构,不过在讲述这个以前,我这里先介绍几个工具,在后文会用到。微信
AndroidStudio升级到版本2.2.3以后提供了Analyze APK的功能(不过做为AS粉想必已经升到AS2.3了吧,哈哈),咱们能够借助该工具清楚的了解APK的下载大小、解压以后的大小、内部各个文件夹或文件占用的大小等信息,进而得知那些地方能够优化。下图为目前达人店APK的内部信息:网络
另外使用该工具还能够反编译资源文件、还原layout中的资源id,分析DEX、显示每个文件夹或文件的方法数,分析哪些第三方库方法数不少但实际只用到了一部分等其余功能。
最后如何使用该工具?有如下两种方式:
NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式:
其中静态分析能够分析出APK安装包中大文件排行榜,各类知名SDK的大小以及占代码总体的比例,各类类型文件的大小以及占排行,各类知名SDK的方法数以及占全部dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。
其中动态分析能够测量生成的速度、网络、内存和磁盘使用率。
我用了一下上面这个工具,感受仍是不错的,下面咱们来看几张有逼格的图(图中数据来自于达人店APK):
APK内部各种型对应文件占用的大小
APK内部各个类库的方法数
APK启动过程当中各个方法的执行时间
ClassShark 是一款查看Android执行文件(apk)的浏览工具,目前有两个android App(Apk)和桌面(jar)的版本。
使用这款工具,能够很方便的打开APK、Class、Jar、res等文件和分析里面的内容。
不过目前这个工具并不须要咱们单独使用,由于AS内部的分析工具就是用的这个。
经过以上工具,咱们能够方便快速的分析APK,找出那些能够优化的部分,为了更加有条理的进行讲解,下面我会按照APK的目录结构来逐条阐述。
上述表格左边部分是APK内部默认存在的文件夹或文件,表格右边对于各个文件夹或文件进行了简要的概述,详细的说明会在以后根据左边的顺序一一讲解。
assets目录用于存放须要打包到应用程序的静态文件,它包含如下几个特性:
获取资源须要使用/assets开始(不包含它)的相对路径名,具体代码以下所示:
String fileNames[] = context.getAssets().list(path);复制代码
那么,assets目录下均可以放什么文件呢?
说实在的,assets目录能够存放各类文件,不过正常状况下,通常只存放如下几种文件:字体文件、WEB页面、配置文件、某些图片。
上述几种文件除了配置文件以外,咱们均可以进行适当的压缩处理:
字体文件
:可使用字体资源文件编辑神器Glyphs进行压缩,其压缩方式其实就是经过删除不须要的字符从而减小APK的大小。
WEB页面
:能够考虑使用7zip压缩工具对该文件进行压缩,在正式使用的时候解压
某些图片
:可使用tinypng进行图片压缩, 目前tinypng已经支持png和jpg图片、.9图的压缩
lib目录用于存放经过C或C++编写编译生成的so文件(native库/JNI开发),其基本目录结构以下图所示:
由于目前市场上主流的架构还只是arm架构,因此若是不是必要的话,能够考虑不支持x86和mips架构,但这并不意味着CPU是x86或mips架构的手机就不能正常安装使用APK了,由于放在arm目录下的so库是能够兼容到其余架构的;
另外arm架构中的eabi-v7a相比于eabi只是在图形渲染方面有了很大的改进,因此若是so库对图形渲染没有很高的要求的话,彻底能够把so库只存放在arm eabi目录中,这样能够大大减少APK的体积。
上面的说明可能比较笼统,下面就拿淘宝、微信的APK来讲明一下:
能够看出淘宝和微信也是这样处理,因此咱们其实也能够这样操做。
res目录用于存放应用程序的资源文件,主要包括布局文件、图片、XML配置文件等,它包含如下几个特性:
其基本目录结构以下图所示:
上图比较全面的列举了res目录下常常包含的子目录和文件,并解释了各个子目录或文件的含义。
根据上图咱们显然能够发现,res目录就是咱们APK瘦身里面的一大重要部分了,因为其包含的知识很大,下面我会分章节进行阐述,尽可能一一阐述清楚。
首先我给出这么一个结论:一个APK尽可能只用一套图片,从内存占用和适配的角度考虑,建议放在xhdpi文件夹下。
那么为何要把图片放在xhdpi文件夹下面呢?
因为此处知识涉及到Android屏幕适配方面的知识,比较复杂。本文是关于APK瘦身的,因此就不详细讲解了。下面进行必要的阐述,首先让咱们来看两张图:
从上面两张图咱们能够获得如下两点信息:
可能有人要问了,获得这两点信息有什么用?
恩,单单这样看并不能知道什么,为了帮助你们了解,我绘制了下面这样表格。不过在展现表格以前,我先为你们灌输两个知识点:
一、ppi计算公式
二、在Android设备中,dpi 等价于 ppi
恩,此处请停留10秒......下面展现我绘制的表格:
有两个注意点:
根据上述表格咱们能够清楚的知道iOS流行的设备分辨率正好对应Android流行的设备分辨率,那么知道这又有什么用?
除了一开始给出结论的内存和适配因素外,更重要的是能够节省设计资源和工做量。在如今的App开发中(iOS和Android),有些设计师为了保持iOS和Android的体验交互一致,可能会以iPhone手机为基础进行设计,包括后期的切图之类的。
不过根据上述表格,咱们不是应该使用两套图吗?这里因为在Android设备中xhdpi和xxhdpi目录下的图片显示效果差别不大,因此彻底能够只使用一套图。
如今咱们的设备中只有一套图了,接下来该怎么为APK瘦身呢?
当咱们从设计师手中获得设计稿以后,以后的工做显然就是切图了,若是设计师切好图就更好了,获得图片以后咱们必定要记得压缩
。
若是图片类型是.png或.jpg的话,咱们可使用tinypng进行压缩;若是图片类型是.gif的话,我建议使用PS进行压缩或裁剪,若是你不会PS的话,可让设计师帮忙。
若是你对图片压缩质量不满意的话,还能够考虑使用不带alpha值的jpg图片、9Patch图片、同等质量下文件更小的WebP图片格式、或者使用SVG替换某些图片资源等其余方式。
下面对于最后两种方式进行简要的阐述:
首先是WebP
:AndroidStudio自2.3版本以后提供了Convert to WebP的功能,选中res目录后右击滑到底部便可看到此功能,下面的引用讲述了WebP的概念和支持度:
WebP是Google新推出的影像技术,它可以让网页图档有效进行压缩,同时在质量相同的状况下,WebP格式图像的体积要比JPEG格式图像小40%,进而让总体网页下载速度加快。为了改善JPEG的图片压缩技术,他们使用了一种基于VP8编码的图片压缩器,利用预测编码技术,同时还采用了一种基于RIFF的很是轻量级的容器。这种容器只会给每张图片增长20字节,但能让图片做者保存他们想要存储的元数据。
android一样做为google的产品,minsdk为4.0即api14以上就能够支持webp,可是对透明的图片会存在一些问题,minsdk为4.2.1+即api17能够完美支持webp。
考虑目前市场4.2.1如下的手机占比已经很是稀少,采用webp格式代替jpg、png的方案很是可行。
下面展现一下收益页面背景图片png形势下和WebP形势下各自的大小:
其次是SVG
:SVG是可缩放矢量图形(Scalable Vector Graphics),它使用XML格式定义图像,可用于替换APK中的图标。
咱们开发者不须要会如何制做,这是设计师的工做,固然设计师也不会傻乎乎经过写代码的方式绘制图标,显然是使用软件制做的。
目前达人店也使用了SVG,不过它的表现形式是阿里巴巴提供的iconfont,它不只容许设计师本身制做SVG图片上传,也提供了百万种图标供选择。
通过上述的方式处理图片,想必在图片质量方面已经机关用尽了,因此咱们只能经过删除无用图片的方式来为APK瘦身了,固然接下来要介绍的两种方式可不只仅是减小图片,应该称之为减小资源
。
这里有个问题:为何会出现无用的资源呢?我认为有如下几种状况:
那么,既然知道了缘由,就应该想出办法去解决,首先是最简单的办法,咱们只须要在主模块的gradle文件中配置shrinkResources
便可,具体配置方法以下图所示:
这就是为何说它是最简单的减小资源方法的缘由。
固然,由于简单必然存在缺陷,由于它只能从打包的应用程序和第三方代码库中删除未被引用的资源。若是在不一样的资源文件夹下面有同名的资源文件,那么就没有办法删除了,即便该资源真的没有被使用。
因此,没有好的办法了,目前我能想到的只能是手动删除了,咱们能够经过AndroidStudio提供的Remove Unused Resources功能预览删除真正未使用的资源,选中res目录右击选择Refactor-Remove Unused Resources...选项以后会出现下面这样的图:
而后根据查找到结果,逐条双击打开文件,按快捷键fn+option+F7进行查找,而后在分析是否应该删除,注意:此处定要慎重,务必自测!!!。
另外,咱们还能够经过将某些图片转换网络图片的方式解决,可是这个操做也是须要慎重的,最重要的一点是不能影响用户的体验。这边路飞同窗制做了一个Chrome插件来帮助咱们快速上传图片到cdn中,能够经过此处下载:joyuploader。
最后,还有一种办法:使用AndroidStudio提供的Lint工具对工程作静态代码检查,它不只能够找出咱们在代码编写上面的失误,还可以列出那些未被使用的资源,甚至还能够指出哪些地方可能存在内存泄漏等等,功能很是庞大,因此若是工程较大的话,仍是比较耗时的,固然这并非问题,由于咱们能够针对某个模块执行静态代码检查。咱们能够经过选中菜单栏Analyze-Inspect Code...选项执行静态代码检查,执行完成的效果图以下所示:
目前咱们不只在资源(特指图片)质量方面作到了极致,在资源数量方面也作到了极致,看起来真的到极致了。其实否则,咱们还可使用资源混淆的方式为APK瘦身,经过压缩文件内容,减小文件名长度的方式实现。
目前比较出名的资源混淆方式是微信的AndResGuard和美团的修改AAPT,不过因为美团的资源混淆方法很是麻烦,还有若是须要经过getIdentifier
的方式获取资源时须要保证这些资源的名字不被混淆,美团很难实现,因此目前你们都在用微信的资源混淆方式,由于微信使用方便,并且提供了白名单。
下面是达人店APK未使用微信资源混淆和使用了微信资源混淆的差别:
能够清楚的看到,在使用了微信资源混淆以后,APK减小了0.7MB左右,效果仍是十分明显的。
那么为何使用了微信资源混淆以后能够实现APK瘦身呢?下面这两张图清楚的展现了瘦身的缘由(摘自微信资源混淆官方文档):
总结,安装包大小减小的缘由如下四个:
相对的,可获得影响效果的因素有如下几个:
能够说,直到如今,咱们对于资源的压缩猜到了一个极致,下面是一些针对于某些资源的压缩办法。
终于讲完了res部分,码字好累,接下来就相对轻松了,首先咱们要讲解的是classes.dex文件。
首先,什么是classes.dex?classes.dex文件是Android系统的可执行文件,包含应用程序的所有操做指令以及运行时数据。
这里稍微介绍一下classes.dex文件的生成过程及对应的命令:
java源代码 -> class字节码:[javac -source 1.6 -target 1.6 com/package1/*.java com/package2/*.java]
class字节码 -> jar包:[jar cvf abc.jar com/package1/*.class com/package2/*.class]
jar包 -> dex文件:[dx --dex --output ***/abc.jar ***/abc_dex.jar],***是abc.jar的绝对路径。
从jar包到dex文件的过程是经过dx工具完成的,它的目的是使各个类可以共享数据,在必定程度上下降了冗余,同时也使文件结构更加紧凑,实验代表,dex文件是传统jar文件大小的50%左右,具体表现形式能够看下图:
既然dx工具已经办帮咱们压缩了那么多,那咱们还有什么好压缩的呢?
固然有,由于dx工具并无压缩class的内容,因此咱们的源代码并无获得压缩,下面我先介绍两种常见的办法来解决这个问题:
和以前最简单的资源压缩方式同样,咱们也能够在主模块的gradle文件中配置minifyEnabled
实现代码混淆,从而减少java文件的大小。由于混淆后的代码将较长的文件名、实例、变量、方法名等等作了简化,从而实现字节长度上的优化。
咱们也可使用以前所说的AndroidStudio提供的Lint工具执行静态代码检查,进而删除无用的类、方法、变量等。
上面的两种方式虽然可以帮助咱们减小APK的大小,可是实际上咱们还能够作的更多。由于上面两种方式是自动化的,因此必然不像人那么智能。
有时候咱们可能遇到这样的状况:咱们引用了一个第三方类库,可是只用到了其中的几个功能,其余的大部分功能一直不用,这不是白白的浪费了用户的流量,下降了APK的下载转化率么?为了解决这样的问题,咱们必须借助一些工具去找到这样的类库,好比在文章开头介绍过的工具:NimbleDroid,下面是达人店APK目前存在的某个这样的第三方类库:
该类库在达人店APK中只用到了扫一扫和生成二维码这两个功能,然而这个类库定义的方法数居然有1428
,显然不合理,彻底能够抽离其内部的扫一扫和生成二维码代码,单独实现。
最后介绍一下Facebook开源的一个减小APK大小以提升性能的工具 -- ReDex,它经过内嵌以及清除僵尸代码这样的优化方式来减小字节码,其主要是对DEX作了优化,可以让APK运行更快,不过须要多测试是否会崩溃。
下面分别是ReDex的教程地址和GitHub地址:
facebook/redex: A bytecode optimizer for Android apps
我我的因为时间问题,目前还未接入该工具,下面就展现一下网友u012124438的测试数据好了:
后来我在使用 Redex 压缩和优化 Android APK一文的帮助下,成功的安装并使用了ReDex,可是发现了如下两个问题:
最后就是对于resources.arsc文件的压缩处理了,其实这里咱们也不须要作什么,首先由于该文件记录的是资源文件和资源ID的映射关系,并无什么值得压缩的地方;另外若是真的有值得压缩的地方,也只有资源文件的名字了,不过这个早就在以前由于咱们使用了微信资源混淆解决了,因此此处就再也不多讲了。
至此,针对于APK目录结构,逐条分析如何为APK瘦身已经讲完了,接下来介绍一下其余的压缩方式。
虽然咱们上面很好的使用了resource shrinker能够回收一些未使用的资源(v七、v四、google Service 等Libarry资源),但有些资源仍然未被清除。
例如:那些未使用的多套替代资源,或者是library内部隐患着引用着的资源而咱们却没有使用到。或者是咱们要根据用户的手机去提供不一样版本的APK,如分辨率(xxhdpi,mhdpi等),so库等。那么咱们可使用APK Splits大大的减小一些无用的资源,这里咱们主要参考了tools.android.com/tech-docs/n…文档。
Multiple APK Support是一个在Google Play,能够发布不一样的应用程序,分别针对不一样的设备配置特征。每一个APK是一个完整的、独立的应用程序版本,但他们分享在Google Play相同的应用程序清单,必须共享相同的包名和与签名。Google Play 会自动给你匹配相应的APK,这样咱们的APK 就能够是分不一样版本构建须要资源文件,从而减少APK的大小。
咱们能够在项目中使用资源动态加载形式,例如:表情,语言,离线库等资源动态加载,减少APK的大小。
将来对于一些独立业务模块,能够作成插件化动态加载,用户须要使用时,只需下载少部分插件。
特别是在扁平化盛行的当下,不少纯色的渐变的圆角的图片均可以用shape实现,代码灵活可控,省去了大量的背景图片。
相信你的工程里也有不少selector文件,也有不少类似的图片只是颜色不一样,经过着色方案咱们能大大减轻这样的工做量,减小这样的文件。
借助于android support库可实现一个全版本兼容的着色方案,参考代码:DrawableLess.java
若是你不是一个Android开发人员,对于以前的阐述可能很是模糊,并不能明显的表现出APK瘦身这件事情的意义,下面我就来总结一下:
首先,针对于达人店APK作了哪些应用?
字体文件使用Glyphs进行压缩,图片使用tinypng进行压缩
只是用一套so文件
只使用一套图
使用WebP图片或SVG图片替换某些PNG或JPG图片
使用Google提供的Gradle插件实现代码混淆和资源混淆
借助NimbleDroid、AS Anylze/Lint工具分析查找删除不须要的代码或资源
使用微信提供的资源混淆工具(处于稳定性测试阶段)
使用Facebook提供的ReDex工具(处于调研阶段)
其次,APK瘦身的效果如何?
这里咱们就直接看图好了:
能够明显的发现,达人店APK愈来愈小了,因此说,这个工做是颇有意义的。
最后,作一下竞品分析?
根据上面图表,能够发现咱们的APK的大小是十分有优点的,可以很好的提升下载转化率。而iOS那边显然就比较大了,虽然二者之间不能比较,可是仍是能够进行一系列优化的。
最后的最后,我想对你们说:在APK瘦身的道路上,必定要掌握好度
,安排好事情的优先级,若是目前要作的事情、要优化的方面比较复杂,不只须要花费很长的时间,并且最终效果也不明显,能够考虑以后再作,甚至不作。