随着业务复杂度的逐渐增加,代码、资源也在不断的增加,此时你的APP大小也在增加。从用户层面来说,面对动辄几十兆的APP来说在非WIFI情况下还是会犹豫要不要下载,不下载你就可能因此失去了一个用户。从公司层面来讲,流量就是钱,减少APP的大小就显得尤为重要。从开发者层面上来讲,你掌握了这个手艺也会略显逼格满满。

废话不多说了,开始正题。

01 APK结构的那些事

知己知彼,方能百战不殆。了解应用程序APK的结构对于我们来说很有帮助。APK文件由一个ZIP存档组成,其中包含组成应用程序的所有文件。这些文件包括Java类文件,资源文件和包含编译资源的文件。

APK包含以下目录:

  • META-INF/:包含CERT.SFCERT.RSA签名文件以及MANIFEST.MF 清单文件。
  • assets/:包含应用可以使用AssetManager对象检索的应用资源。
  • res/:包含未编译到的资源 resources.arsc
  • lib/:包含特定于处理器软件层的编译代码。该目录包含了每种平台的子目录,像armeabiarmeabi-v7aarm64-v8ax86x86_64,和mips
  • resources.arsc:包含已编译的资源。该文件包含res/values/ 文件夹所有配置中的XML内容。打包工具提取此XML内容,将其编译为二进制格式,并将内容归档。此内容包括语言字符串和样式,以及直接包含在resources.arsc文件中的内容路径 ,例如布局文件和图像。
  • classes.dex:包含以Dalvik / ART虚拟机可理解的DEX文件格式编译的类。
  • AndroidManifest.xml:包含核心Android清单文件。该文件列出应用程序的名称,版本,访问权限和引用的库文件。该文件使用Android的二进制XML格式。

来看看淘宝APP的unzip之后的文件目录

 
taobao_app.png

一般来讲APK结构中比较大的部分一般是classes.dex、lib、res、assets这些文件或者目录。所以接下来将会针对这四种情况进行讲解。

另外,我们通过APK Analyser 可以分析 APK

 
APK Analyser.png

02 减小 classes.dex

classes.dex 包含了所有 Java 代码。当你编译你的应用时,gradle 会将你的所有模块里的 .class 文件转换成 .dex 文件并将这些文件合成一个 classes.dex 文件。

单个的 classes.dex 文件可以容纳大约 64K 方法。如果你达到了这个限制,你必须要在你的工程中启用 multidexing。这将会创建另一个 classes1.dex 文件去存储剩下的方法。所以 classes.dex 文件数目由你的方法数而定。

减少第三库的使用

随着业务的频繁变更以及复杂度的增加,我们往往会使用第三方Libaray,有时候我们可能仅仅用到了很少一部分的功能,这个时候就需要慎重考虑完全引用。从我的开发经验上来讲,宁愿参照自己去实现,也不愿意多引入一个第三方库。

避免枚举

一个枚举可以为您的应用程序的classes.dex文件添加大约1.0到1.4 KB的大小 。这些添加可以快速累积到复杂系统或共享库。如果可能,请考虑使用@IntDef注释,这种类型转换保留了枚举的所有类型安全优势。

使用ProGuard

下面这段来自 build.gradle 文件的代码用于为发布构建启用代码压缩:

android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
...
}

除了 minifyEnabled 属性外,还有用于定义 ProGuard 规则的 proguardFiles 属性:

getDefaultProguardFile('proguard-android.txt') 方法可从 Android SDK tools/proguard/ 文件夹获取默认的 ProGuard 设置。
提示:要想做进一步的代码压缩,请尝试使用位于同一位置的 proguard-android-optimize.txt 文件。它包括相同的 ProGuard 规则,但还包括其他在字节码一级(方法内和方法间)执行分析的优化,以进一步减小 APK 大小和帮助提高其运行速度。

proguard-rules.pro 文件用于添加自定义 ProGuard 规则。默认情况下,该文件位于模块根目录(build.gradle 文件旁)。

03 优化assets和res中的资源文件

题外话

res/raw和assets的相同点:

  1. 两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。

res/raw和assets的不同点:

  1. res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
  2. res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹。

针对不同的情况,对于资源文件有不同的优化策略。一般来讲,对于res/drawable-**ddpi中的png资源可以进行压缩。

图片资源优化策略

格式压缩

使用TinyPng或者Guetzli进行压缩。Guetzli的使用可以参见我之前写的博文在Mac上使用Google图片压缩工具Guetzli

使用WebP文件格式

定位Android 3.2(API级别13)或更高级别时 ,您也可以使用WebP文件格式来制作图像,而不是使用PNG或JPEG文件。WebP格式提供有损压缩(如JPEG)以及透明度(如PNG),但可以提供比JPEG或PNG更好的压缩。

Android 4.0 (API level 14) 支持有损压缩的WebP格式,Android 4.3 (API level 18) 开始支持无损透明WebP图像。

看下图:

 
webp_compress_performance.png

压缩效率极高,仅为PNG格式的12%。惊喜不惊喜。。。

使用矢量图形

您可以使用矢量图形来创建与分辨率无关的图标和其他可伸缩媒体。使用这些图形可以大大减少您的APK足迹。矢量图像在Android中表示为VectorDrawable对象。通过一个VectorDrawable对象,一个100字节的文件可以生成一个与屏幕尺寸一致的清晰图像。

但是,系统渲染每个 VectorDrawable对象需要很长时间,而较大的图像需要更长的时间才能显示在屏幕上。因此,只有在显示小图像时才考虑使用这些矢量图形。

其它策略

有时候我们可能对一张图片进行重复利用,比如一张图片仅仅是整体颜色的变换可以使用setColorFilter或者tint。尽量减少使用帧动画,那可是一堆图片呀。

压缩资源

要启用资源压缩,请在 build.gradle 文件中将 shrinkResources 属性设置为 true。

android {
...
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}

资源压缩器目前不会移除 values/ 文件夹中定义的资源(例如字符串、尺寸、样式和颜色)。这是因为 Android 资源打包工具 (AAPT) 不允许 Gradle 插件为资源指定预定义版本。

同时,我们也可以指定哪些资源可以保留下来。

例如,将下边的代码保存在 res/raw/keep.xml。构建不会将该文件打包到 APK 之中。

<?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" />

resources有以下属性:

tools:keep 指出哪些资源会保留
tools:discard 指定哪些资源需要剔除
tools:shrinkMode 资源压缩模式,有两种取值strict和safe,默认为safe

safestrict的优化策略:

safe可以简单理解为安全模式,它会尽最大努力检查代码中可能会使用到的资源进行保留,避免运行时错误。

如果你的代码调用 Resources.getIdentifier(),这就表示你的代码将根据动态生成的字符串查询资源名称。当你执行这一调用时,默认情况下资源压缩器会采取防御性行为,将所有具有匹配名称格式的资源标记为可能已使用,无法移除。

String name = String.format("img_%1d", angle + );
res = getResources().getIdentifier(name, "drawable", getPackageName());

img_ 前缀的资源标记为已使用。

在strict模式下,img_前缀的资源会做未使用的处理,因此你需要使用tools:keep手动进行已使用标识。

移除未使用的备用资源

我们知道google给我们的apk提供了国际化支持,如适应不同的屏幕分辨率的drawable资源,还有适应不同语言的字符串资源等等,但是在很多情况下我们只需要一些指定分辨率和语言的资源就可以了,这个时候我们可以使用resConfigs方法来配置。

defaultConfig {
// 对于国际化支持只打包中文资源,
resConfigs "zh-rCN"
}

04 lib中资源优化

这里我们主要讲一下lib中动态链接库的优化策略,也就是SO文件。如果你有NDK的开发经验可能会更容易理解一些。

为了支持不同指令集的情况,应用可能会包含armeabi、armeabi-v7a、x86的SO文件等。

目前主流的机型都是支持armeabi-v7a的,并且armeabi-v7a兼容armeabi。所以在一般的开发中我们只需要使用armeabi-v7a 进行ABI支持。

有些SO库可以采用网络下载,把负担放到用户安装完应用之后。对于哪些SO文件可以放到网络中加载,还需要看具体业务情况。

题外话,如果运行时找不到SO的话,会导致应用崩溃。

java.lang.UnsatisfiedLinkError: Couldn't load stlport_shared from loader dalvik.system.PathClassLoader: findLibrary returned null
at java.lang.Runtime.loadLibrary(Runtime.java:)
at java.lang.System.loadLibrary(System.java:)
at com.your.app.NativeClass.<clinit>(Native.java:)
... more Caused by: java.lang.UnsatisfiedLinkError: Library stlport_shared not found
at java.lang.Runtime.loadLibrary(Runtime.java:)
at java.lang.System.loadLibrary(System.java:)
at com.your.app.NativeClass.<clinit>(Native.java:)
... more

我们也是有办法应对的,可以参见这个开源项目ReLinker

另外关于SO的优化我会单独拿出来讲一讲。

原文链接:https://www.jianshu.com/p/5921e9561f5f

你必须要懂的APK瘦身知识的更多相关文章

  1. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  2. APK瘦身记,如何实现高达53%的压缩效果

    作者:非戈@阿里移动安全 1.我是怎么思考这件事情的 APK是Android系统安装包的文件格式,关于这个话题其实是一个老生常谈的题目,不论是公司内部,还是外部网络,前人前辈已经总结出很多方法和规律. ...

  3. APK瘦身实践

    首发地址:http://www.jayfeng.com/2015/12/29/APK%E7%98%A6%E8%BA%AB%E5%AE%9E%E8%B7%B5/ 因为推广的需要,公司需要把APK的大小再 ...

  4. APK瘦身

    APK瘦身 主要从一下三方面来瘦身: 1. Java 源代码 1) ,这方面主要是通过最简洁的代码实现最直接的功能,还有就是提出上线前不必要的java代码,可以使用UCDector进行分析,从而对代码 ...

  5. Android APK 瘦身 - JOOX Music项目实战

    导语 JOOX Music是腾讯海外布局的一个音乐产品,2014年发布以来已经成为5个国家和地区排名第一的音乐App.东南亚是JOOX Music的主要发行地区,由于JOOX Music所面对的市场存 ...

  6. Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

  7. Android性能优化系列之apk瘦身

    Android性能优化系列之布局优化 Android性能优化系列之内存优化 为什么APK要瘦身.APK越大,在下载安装过程中.他们耗费的流量会越多,安装等待时间也会越长:对于产品本身,意味着下载转化率 ...

  8. 39、apk瘦身(转载)

    本文转自::Android开发中文站 » 关于APK瘦身值得分享的一些经验 从APK的文件结构说起 APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用 ...

  9. 安卓APK瘦身

    之前打包的时候直接就用eclipse或者android studio直接生成签名文件,并没有关心大小问题,近期有人问我有没有对APK进行瘦身.对这方面内容一致没有关注过,今天试用了各种方式把项目签名a ...

随机推荐

  1. css animation和keyframes

    keyframes应用在animation上,animation应用在元素上. <html> <style type="text/css"> .div1 { ...

  2. ios7新特性--1

    1.用户界面的扁平化 2.UIKit 动态行为支持 应用程序可以设置UIView 对象和其他对象(遵从UIDynamicItem 协议)的动态行为属性.遵从UIDynamicItem协议的对象被称为d ...

  3. [na]tcpdump非常实用的抓包实例

    基本语法篇 常用应用: 过滤物理口 过滤某个port/ip/mac 过滤协议 显示ip/mac/port不解析等 过滤mac tcpdump -i eth0 ether host 24:DF:6A:F ...

  4. TaskAsyncHelper

    public static class TaskAsyncHelper { /// <summary> /// 将一个方法function异步运行,在执行完毕时执行回调callback / ...

  5. 解密DNSPOD应对DDoS攻击招式!

    最近,安全专家Incapsula在最新版<DDoS威胁环境报告>指出.现在实施DDoS攻击的人仅仅有两类:一类是专业网络黑客.而还有一类就是所谓的botter. 简言之,booter就是僵 ...

  6. 菜鸟学数据库(六)——方便快捷的开启、关闭Oracle服务

    背景: 作为一个程序员,在日常的工作中,我们电脑经常需要同时运行很多程序,如:Eclipse.浏览器.即时通讯软件等,甚至经常需要打开几个Office文档或者pdf文档.这时候你的内存估计已经爆表了吧 ...

  7. nodejs中aes-128-cbc加密和解密

    和java程序进行交互的时候,java那边使用AES 128位填充模式:AES/CBC/PKCS5Padding加密方法,在nodejs中采用对应的aes-128-cbc加密方法就能对应上,因为有使用 ...

  8. bitcoin双花

    https://en.bitcoin.it/wiki/Irreversible_Transactions https://www.reddit.com/r/Bitcoin/comments/2e7bf ...

  9. Flink connectedstreams

    https://stackoverflow.com/questions/40613684/apache-flink-scope-of-valuestate-in-connectedstreams ht ...

  10. iOS 10.3.3 更新背后的故事

    iOS 10.3.3 更新背后的故事 TLDR:赶紧升级! 苹果最近提示大家将系统升级到 iOS 10.3.3,并且描述这个更新的内容是:修复和改进安全性. iOS 10.3.3 includes b ...