「Android」 APK瘦身探索

本文来自尚妆Android团队青峰
发表于尚妆博客,欢迎订阅。html

APK瘦身探索

最近几周一直在研究如何为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的目录结构,不过在讲述这个以前,我这里先介绍几个工具,在后文会用到。微信

AndroidStudio2.2.3

AndroidStudio升级到版本2.2.3以后提供了Analyze APK的功能(不过做为AS粉想必已经升到AS2.3了吧,哈哈),咱们能够借助该工具清楚的了解APK的下载大小、解压以后的大小、内部各个文件夹或文件占用的大小等信息,进而得知那些地方能够优化。下图为目前达人店APK的内部信息:网络

APK Info

另外使用该工具还能够反编译资源文件、还原layout中的资源id,分析DEX、显示每个文件夹或文件的方法数,分析哪些第三方库方法数不少但实际只用到了一部分等其余功能。

最后如何使用该工具?有如下两种方式:

  1. 直接将APK拖拽进入AndroidStudio便可
  2. 点击菜单栏Build-Analyze APK...选项,再选择须要分析的APK便可

NimbleDroid

NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式:

其中静态分析能够分析出APK安装包中大文件排行榜,各类知名SDK的大小以及占代码总体的比例,各类类型文件的大小以及占排行,各类知名SDK的方法数以及占全部dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。

其中动态分析能够测量生成的速度、网络、内存和磁盘使用率。

我用了一下上面这个工具,感受仍是不错的,下面咱们来看几张有逼格的图(图中数据来自于达人店APK):

APK内部各种型对应文件占用的大小

Size by Type

APK内部各个类库的方法数

Method Count

APK启动过程当中各个方法的执行时间

Hung CPU Methods

ClassShark

ClassShark 是一款查看Android执行文件(apk)的浏览工具,目前有两个android App(Apk)和桌面(jar)的版本。

使用这款工具,能够很方便的打开APK、Class、Jar、res等文件和分析里面的内容。

ClassShark

不过目前这个工具并不须要咱们单独使用,由于AS内部的分析工具就是用的这个。

经过以上工具,咱们能够方便快速的分析APK,找出那些能够优化的部分,为了更加有条理的进行讲解,下面我会按照APK的目录结构来逐条阐述。

APK目录结构

APK目录结构

上述表格左边部分是APK内部默认存在的文件夹或文件,表格右边对于各个文件夹或文件进行了简要的概述,详细的说明会在以后根据左边的顺序一一讲解。

assets目录

assets目录用于存放须要打包到应用程序的静态文件,它包含如下几个特性:

  1. 使用AssetManager类管理资源
  2. assets目录内部文件不会被系统编译
  3. assets目录支持任意深度的子目录
  4. 获取资源须要使用/assets开始(不包含它)的相对路径名,具体代码以下所示:

    String fileNames[] = context.getAssets().list(path);复制代码

那么,assets目录下均可以放什么文件呢?

说实在的,assets目录能够存放各类文件,不过正常状况下,通常只存放如下几种文件:字体文件、WEB页面、配置文件、某些图片。

上述几种文件除了配置文件以外,咱们均可以进行适当的压缩处理:

字体文件:可使用字体资源文件编辑神器Glyphs进行压缩,其压缩方式其实就是经过删除不须要的字符从而减小APK的大小。

WEB页面:能够考虑使用7zip压缩工具对该文件进行压缩,在正式使用的时候解压

某些图片:可使用tinypng进行图片压缩, 目前tinypng已经支持png和jpg图片、.9图的压缩

lib目录

lib目录用于存放经过C或C++编写编译生成的so文件(native库/JNI开发),其基本目录结构以下图所示:

lib目录结构

由于目前市场上主流的架构还只是arm架构,因此若是不是必要的话,能够考虑不支持x86和mips架构,但这并不意味着CPU是x86或mips架构的手机就不能正常安装使用APK了,由于放在arm目录下的so库是能够兼容到其余架构的;

另外arm架构中的eabi-v7a相比于eabi只是在图形渲染方面有了很大的改进,因此若是so库对图形渲染没有很高的要求的话,彻底能够把so库只存放在arm eabi目录中,这样能够大大减少APK的体积。

上面的说明可能比较笼统,下面就拿淘宝、微信的APK来讲明一下:

APK_taobao

APK_weixin

能够看出淘宝和微信也是这样处理,因此咱们其实也能够这样操做。

res目录

res目录用于存放应用程序的资源文件,主要包括布局文件、图片、XML配置文件等,它包含如下几个特性:

  1. 使用Resources类管理资源
  2. res目录内部文件会被系统编译
  3. res目录不支持任意深度的子目录
  4. 获取资源不须要经过相对路径找寻,由于文件会被系统编译,因此须要经过资源ID(注:资源ID被存放在resources.arsc文件中)查找

其基本目录结构以下图所示:

res目录结构

上图比较全面的列举了res目录下常常包含的子目录和文件,并解释了各个子目录或文件的含义。

根据上图咱们显然能够发现,res目录就是咱们APK瘦身里面的一大重要部分了,因为其包含的知识很大,下面我会分章节进行阐述,尽可能一一阐述清楚。

只用一套图

首先我给出这么一个结论:一个APK尽可能只用一套图片,从内存占用和适配的角度考虑,建议放在xhdpi文件夹下。

那么为何要把图片放在xhdpi文件夹下面呢?

因为此处知识涉及到Android屏幕适配方面的知识,比较复杂。本文是关于APK瘦身的,因此就不详细讲解了。下面进行必要的阐述,首先让咱们来看两张图:

201612&201701设备分辨率_Android

201612&201701设备分辨率_iOS

从上面两张图咱们能够获得如下两点信息:

  1. Android主要的设备分辨率为:1280*720、1920*1080
  2. iOS主要的设备分辨率为:1334*750、2208*124二、1136*640

可能有人要问了,获得这两点信息有什么用?

恩,单单这样看并不能知道什么,为了帮助你们了解,我绘制了下面这样表格。不过在展现表格以前,我先为你们灌输两个知识点

一、ppi计算公式

ppi计算公式

二、在Android设备中,dpi 等价于 ppi

恩,此处请停留10秒......下面展现我绘制的表格:

201612&201701设备分辨率_统计分析

有两个注意点:

  1. 表格中上两行表明的是Android的设备分辨率及相关数据,下三行表明的是iOS的设备分辨率及相关数据
  2. Android屏幕密度为320或480是由谷歌规定的,固然这里排除各大厂商自行修改的状况

根据上述表格咱们能够清楚的知道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形势下各自的大小:

WebP压缩效果

其次是SVG:SVG是可缩放矢量图形(Scalable Vector Graphics),它使用XML格式定义图像,可用于替换APK中的图标

咱们开发者不须要会如何制做,这是设计师的工做,固然设计师也不会傻乎乎经过写代码的方式绘制图标,显然是使用软件制做的。

目前达人店也使用了SVG,不过它的表现形式是阿里巴巴提供的iconfont,它不只容许设计师本身制做SVG图片上传,也提供了百万种图标供选择。

减小资源

通过上述的方式处理图片,想必在图片质量方面已经机关用尽了,因此咱们只能经过删除无用图片的方式来为APK瘦身了,固然接下来要介绍的两种方式可不只仅是减小图片,应该称之为减小资源

这里有个问题:为何会出现无用的资源呢?我认为有如下几种状况:

  1. 因为多人协做开发,没有好的规范,致使我的思惟严重,随便添加或删除资源
  2. 目前公司有好的规范,但以前没有,历史遗留问题,没人愿意去管理
  3. 上一版须要该资源,下一版不须要,而后没有删除

那么,既然知道了缘由,就应该想出办法去解决,首先是最简单的办法,咱们只须要在主模块的gradle文件中配置shrinkResources便可,具体配置方法以下图所示:

主模块Gradle配置

这就是为何说它是最简单的减小资源方法的缘由。

固然,由于简单必然存在缺陷,由于它只能从打包的应用程序和第三方代码库中删除未被引用的资源。若是在不一样的资源文件夹下面有同名的资源文件,那么就没有办法删除了,即便该资源真的没有被使用。

因此,没有好的办法了,目前我能想到的只能是手动删除了,咱们能够经过AndroidStudio提供的Remove Unused Resources功能预览删除真正未使用的资源,选中res目录右击选择Refactor-Remove Unused Resources...选项以后会出现下面这样的图:

Remove Unused Resources

而后根据查找到结果,逐条双击打开文件,按快捷键fn+option+F7进行查找,而后在分析是否应该删除,注意:此处定要慎重,务必自测!!!

另外,咱们还能够经过将某些图片转换网络图片的方式解决,可是这个操做也是须要慎重的,最重要的一点是不能影响用户的体验。这边路飞同窗制做了一个Chrome插件来帮助咱们快速上传图片到cdn中,能够经过此处下载:joyuploader

最后,还有一种办法:使用AndroidStudio提供的Lint工具对工程作静态代码检查,它不只能够找出咱们在代码编写上面的失误,还可以列出那些未被使用的资源,甚至还能够指出哪些地方可能存在内存泄漏等等,功能很是庞大,因此若是工程较大的话,仍是比较耗时的,固然这并非问题,由于咱们能够针对某个模块执行静态代码检查。咱们能够经过选中菜单栏Analyze-Inspect Code...选项执行静态代码检查,执行完成的效果图以下所示:

Lint

资源混淆

目前咱们不只在资源(特指图片)质量方面作到了极致,在资源数量方面也作到了极致,看起来真的到极致了。其实否则,咱们还可使用资源混淆的方式为APK瘦身,经过压缩文件内容,减小文件名长度的方式实现。

目前比较出名的资源混淆方式是微信的AndResGuard美团的修改AAPT,不过因为美团的资源混淆方法很是麻烦,还有若是须要经过getIdentifier的方式获取资源时须要保证这些资源的名字不被混淆,美团很难实现,因此目前你们都在用微信的资源混淆方式,由于微信使用方便,并且提供了白名单。

下面是达人店APK未使用微信资源混淆和使用了微信资源混淆的差别:

shopandroid-debug.apk

shopandroid-debug_signed_7zip_aligned.apk

能够清楚的看到,在使用了微信资源混淆以后,APK减小了0.7MB左右,效果仍是十分明显的。

那么为何使用了微信资源混淆以后能够实现APK瘦身呢?下面这两张图清楚的展现了瘦身的缘由(摘自微信资源混淆官方文档):

总结,安装包大小减小的缘由如下四个:

AndResGuard安装包减少的缘由

相对的,可获得影响效果的因素有如下几个:

AndResGuard影响APK大小的因素

能够说,直到如今,咱们对于资源的压缩猜到了一个极致,下面是一些针对于某些资源的压缩办法。

其余资源

  1. 若是raw文件夹下有音频文件,尽可能不要使用无损的音频格式,好比wav。能够考虑相比于mp3同等质量但文件更小的opus音频格式。
  2. 能不用图片的就不用图片,除了经过前面提过的使用9Patch图、iconfont实现外,还可使用shape代码实现,也能够考虑引进VectorDrawable(矢量图) ,从5.0(API等级21)开始,android开始支持矢量图。目前矢量图兼容到API7,矢量图动画兼容到API11。这里就不详细讲解了。

终于讲完了res部分,码字好累,接下来就相对轻松了,首先咱们要讲解的是classes.dex文件。

classes.dex

首先,什么是classes.dex?classes.dex文件是Android系统的可执行文件,包含应用程序的所有操做指令以及运行时数据。

这里稍微介绍一下classes.dex文件的生成过程及对应的命令:

  1. java源代码 -> class字节码:[javac -source 1.6 -target 1.6 com/package1/*.java com/package2/*.java]

  2. class字节码 -> jar包:[jar cvf abc.jar com/package1/*.class com/package2/*.class]

  3. jar包 -> dex文件:[dx --dex --output ***/abc.jar ***/abc_dex.jar],***是abc.jar的绝对路径。

从jar包到dex文件的过程是经过dx工具完成的,它的目的是使各个类可以共享数据,在必定程度上下降了冗余,同时也使文件结构更加紧凑,实验代表,dex文件是传统jar文件大小的50%左右,具体表现形式能够看下图:

jar2dex

既然dx工具已经办帮咱们压缩了那么多,那咱们还有什么好压缩的呢?

固然有,由于dx工具并无压缩class的内容,因此咱们的源代码并无获得压缩,下面我先介绍两种常见的办法来解决这个问题:

  1. 和以前最简单的资源压缩方式同样,咱们也能够在主模块的gradle文件中配置minifyEnabled实现代码混淆,从而减少java文件的大小。由于混淆后的代码将较长的文件名、实例、变量、方法名等等作了简化,从而实现字节长度上的优化。

  2. 咱们也可使用以前所说的AndroidStudio提供的Lint工具执行静态代码检查,进而删除无用的类、方法、变量等。

上面的两种方式虽然可以帮助咱们减小APK的大小,可是实际上咱们还能够作的更多。由于上面两种方式是自动化的,因此必然不像人那么智能。

有时候咱们可能遇到这样的状况:咱们引用了一个第三方类库,可是只用到了其中的几个功能,其余的大部分功能一直不用,这不是白白的浪费了用户的流量,下降了APK的下载转化率么?为了解决这样的问题,咱们必须借助一些工具去找到这样的类库,好比在文章开头介绍过的工具:NimbleDroid,下面是达人店APK目前存在的某个这样的第三方类库:

Method Count Of ZXing

该类库在达人店APK中只用到了扫一扫和生成二维码这两个功能,然而这个类库定义的方法数居然有1428,显然不合理,彻底能够抽离其内部的扫一扫和生成二维码代码,单独实现。

ReDex

最后介绍一下Facebook开源的一个减小APK大小以提升性能的工具 -- ReDex,它经过内嵌以及清除僵尸代码这样的优化方式来减小字节码,其主要是对DEX作了优化,可以让APK运行更快,不过须要多测试是否会崩溃。

下面分别是ReDex的教程地址和GitHub地址:

Open-sourcing ReDex: Making Android apps smaller and faster | Engineering Blog | Facebook Code | Facebook

facebook/redex: A bytecode optimizer for Android apps

我我的因为时间问题,目前还未接入该工具,下面就展现一下网友u012124438的测试数据好了:

ReDex

后来我在使用 Redex 压缩和优化 Android APK一文的帮助下,成功的安装并使用了ReDex,可是发现了如下两个问题:

  1. 用ReDex处理过的APK,其内部的META-INF目录会被删除,虽然能够将ReDex处理过的APK用AndResGuard处理一下,会从新出现该目录。
  2. 用ReDex处理过的APK,安装好以后会出现Crash的问题,目前达人店APK要Crash三次以后才能正常使用,并且它对于达人店APK的贡献只有20~25K,因此并未打算接入。

resources.arsc

最后就是对于resources.arsc文件的压缩处理了,其实这里咱们也不须要作什么,首先由于该文件记录的是资源文件和资源ID的映射关系,并无什么值得压缩的地方;另外若是真的有值得压缩的地方,也只有资源文件的名字了,不过这个早就在以前由于咱们使用了微信资源混淆解决了,因此此处就再也不多讲了。

至此,针对于APK目录结构,逐条分析如何为APK瘦身已经讲完了,接下来介绍一下其余的压缩方式。

其余方式

使用APK Splits构建APK

虽然咱们上面很好的使用了resource shrinker能够回收一些未使用的资源(v七、v四、google Service 等Libarry资源),但有些资源仍然未被清除。

例如:那些未使用的多套替代资源,或者是library内部隐患着引用着的资源而咱们却没有使用到。或者是咱们要根据用户的手机去提供不一样版本的APK,如分辨率(xxhdpi,mhdpi等),so库等。那么咱们可使用APK Splits大大的减小一些无用的资源,这里咱们主要参考了tools.android.com/tech-docs/n…文档。

使用多版本的APK

Multiple APK Support是一个在Google Play,能够发布不一样的应用程序,分别针对不一样的设备配置特征。每一个APK是一个完整的、独立的应用程序版本,但他们分享在Google Play相同的应用程序清单,必须共享相同的包名和与签名。Google Play 会自动给你匹配相应的APK,这样咱们的APK 就能够是分不一样版本构建须要资源文件,从而减少APK的大小。

资源动态加载

咱们能够在项目中使用资源动态加载形式,例如:表情,语言,离线库等资源动态加载,减少APK的大小。

支持插件化

将来对于一些独立业务模块,能够作成插件化动态加载,用户须要使用时,只需下载少部分插件。

使用shape背景

特别是在扁平化盛行的当下,不少纯色的渐变的圆角的图片均可以用shape实现,代码灵活可控,省去了大量的背景图片。

使用着色方案

相信你的工程里也有不少selector文件,也有不少类似的图片只是颜色不一样,经过着色方案咱们能大大减轻这样的工做量,减小这样的文件。

借助于android support库可实现一个全版本兼容的着色方案,参考代码:DrawableLess.java

其余方式...

总结

若是你不是一个Android开发人员,对于以前的阐述可能很是模糊,并不能明显的表现出APK瘦身这件事情的意义,下面我就来总结一下:

首先,针对于达人店APK作了哪些应用?

  1. 字体文件使用Glyphs进行压缩,图片使用tinypng进行压缩

  2. 只是用一套so文件

  3. 只使用一套图

  4. 使用WebP图片或SVG图片替换某些PNG或JPG图片

  5. 使用Google提供的Gradle插件实现代码混淆和资源混淆

  6. 借助NimbleDroid、AS Anylze/Lint工具分析查找删除不须要的代码或资源

  7. 使用微信提供的资源混淆工具(处于稳定性测试阶段)

  8. 使用Facebook提供的ReDex工具(处于调研阶段)

其次,APK瘦身的效果如何?

这里咱们就直接看图好了:

APK瘦身效果

能够明显的发现,达人店APK愈来愈小了,因此说,这个工做是颇有意义的。

最后,作一下竞品分析?

竞品分析

根据上面图表,能够发现咱们的APK的大小是十分有优点的,可以很好的提升下载转化率。而iOS那边显然就比较大了,虽然二者之间不能比较,可是仍是能够进行一系列优化的。

忠告

最后的最后,我想对你们说:在APK瘦身的道路上,必定要掌握好,安排好事情的优先级,若是目前要作的事情、要优化的方面比较复杂,不只须要花费很长的时间,并且最终效果也不明显,能够考虑以后再作,甚至不作。

参考连接

  1. 【Android技术专题】APK瘦身看这一篇文章就够了
  2. 那些你不知道的 APK 瘦身,让你的 APK 更小
  3. Android 性能优化系列 之 apk 瘦身
  4. APK文件结构和安装过程
  5. Android开发之资源目录assets与res/raw的区别分析
  6. Android开发之assets目录下资源使用总结
  7. Android开发:最全面、最易懂的Android屏幕适配解决方案
  8. 是时候使用 webp 给 apk 瘦身了!
  9. Dex文件格式详解
  10. Android应用瘦身,从18MB到12.5MB
相关文章
相关标签/搜索