[Android 之美] 那些你不知道的APK 瘦身,让你的APK更小

[Android 之美] APK 瘦身,减少APK的大小

让你的apk文件尽可能小,应该使移除未使用的代码和资源文件。那么本章节介绍了如何做到让APK更小,性能更好,下载转化率会更高,以及如何指定在构建APK过程中保留或移除的代码和资源,在我们还没有分析APK大小之前,项目中存在一些资源放置处理不当,没有统一的规范,依赖管理不合理,资源重叠,dex方法数过多等问题,导致APK文件比较大,公司要求APK体积大小要优化到3M左右。经过我们的努力终于达到要求,然而我们发现还能再小。

  • Android 之美 APK 瘦身减少APK的大小
  • 分析APK的大小
      • 使用Android Studio 22
      • NimbleDroid
      • ClassShark
  • 对APK进行瘦身
    • 对资源进行极限压缩

      • 使用WEBPSVG图片资源格式
      • 利用AndResGuard资源压缩打包工具
    • 清除你的代码和资源
      • 清除无用代码

          • 自定义混淆规则
      • 清除无用的资源文件
          • 指定要忽略的资源文件
          • 启用严格的检测
      • 清除未使用的替代资源
      • 合并重复的资源
        • 查看资源回收
    • 使用APK Splits构建APK
      • 按屏幕密度拆分
      • 按 ABI 拆分
      • 使用多版本的APK
    • 资源动态加载
    • 依赖库优化
    • 支持插件化
    • 使用ReDex优化

分析APK的大小

正所谓工欲善其事,必先利其器,我们得现有利器,下面就是我们常用的分析APK大小工具的利器。

使用Android Studio 2.2

Android Studio 2.2 新功能直接能分析APK的大小,双击打开就能看到那些占用APK比例大,方法数等。

  • 分析任何的APK
  • 查看APK下载包的大小,解压后的实际大小
  • 反编译资源文件,还原layout中的资源id,代码
  • 分析dex,显示每部分的方法数,直接查看那些library体积比较大

使用方法:Build -> Analyz APK

有了Analyz APK这个利器,以下工具也可以基本不用了

NimbleDroid

NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的自动化分析Android app性能指标的系统,分析的方式有静态和动态两种方式,其中静态分析可以分析出APK安装包中大文件排行榜,各种知名SDK的大小以及占代码整体的比例,各种类型文件的大小以及占排行,各种知名SDK的方法数以及占所有dex中方法数的比例,针对缓慢的方法,缓慢的第三方SDK和内存泄漏。

测量生成的速度、网络、内存和磁盘使用率。总之有非常多分析App性能的功能,如果要做性能优化,也可以尝试使用NimbleDroid

查看详细的方法耗时

具体使用方法请看官网:

https://nimbledroid.com/

不过需要注意的是不要上传任何未发布的产品

ClassShark

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

使用这款工具,可以很方便的打开APK/Class/Jar/res

等 文件和分析里面的内容。

具体源码与使用方法详细在github中:

https://github.com/google/android-classyshark

通过以上任一工具分析我们知道我们项目中主要是以下文件占用APK大小:

  • classes.dex

    classes.dex是java源码编译后生成的java字节码文件,

  • res

    主要是存放我们的图片资源

  • resources.arsc

    编译后的二进制资源文件,非常多无效资源文件(语言)

  • assets

    主要存放了我们的缓存数据文件,已做最优化压缩,我们考虑能否云端存放。

  • lib

    主要是存放我们的so库,目前我们已经优化了

既然知道了那些数据导致我们APK体积大,那么我们就着手瘦身了。

对APK进行瘦身

对资源进行极限压缩

对资源进行极限压缩,主要是如res里面用到的图片资源文件和assets的html,db等一些缓存预留在APK的数据文件

  • assets资源压缩,使用7zip或者lzma压缩方式最高
  • res 图片资源的压缩,使用tinypng优化Android的资源图片,通常我们可以在保证图片不失真的情况下,多压缩几次。目前tinypng已经支持png和jpg图片、.9图的压缩
  • 将非alpha的图转换成jpg形式

通过以上方法我们图片降低了79%的大小。

使用WEBP,SVG图片资源格式

  • WebP是谷歌研发出来的一种图片数据格式,它是一种支持有损压缩和无损压缩的图片文件格式,如果应用支持到Android 4.0+,那么我们可以使用WebP格式代替PNG,我们的资源大小能降低50%多。或者有些资源可以使用SVG图片资源更小。

    以下是他们的对比图:

这里提供方便转换的WEBP资源的工具:

利用AndResGuard资源压缩打包工具

微信中的资源混淆工具主要为了混淆资源ID长度(例如将res/drawable/icon.png,png变成混淆为r/s/a.png),同时利用7z深度压缩、对png的存储方式做了改变占用内存更小,大大减少了安装包体积

具体源码与使用方法详细在github中:

https://github.com/shwenzhang/AndResGuard

清除你的代码和资源

通过上面的图片资源压缩能对APK减小不少,但这往往还不够,项目里还有很多未使用的资源文件,重复的资源等,这里主要参考Google官方文档https://developer.android.com/studio/build/shrink-code.html#shrink-code 部分,利用Android Plugin开启gradle 的Code shrinkingProGuard结合使用。

  • ProGuard能够检测和删除未使用的类,字段,方法,和从你的打包应用程序的属性,包括那些包含代码库,ProGuard是一个混淆优化字节码的工具,能够删除一些未使用的代码,混淆使用的类,字段,方法和短名称,经过混淆处理也能够使APK源代码得到保护
  • Code shrinking是一个Android Plugin for Gradle,从您的打包的应用程序中删除未使用的资源,包括代码库中的未使用的资源。它工作在与代码缩小,这样,一旦未使用的代码已被删除,任何资源不再引用可以安全地删除。

该功能需要依赖于:

  • SDK Tools 25.0.10 或更高
  • Android Plugin for Gradle 2.0.0 或更高

清除无用代码

code shrinking需要结合ProGuard使用,添加minifyEnabled true在你的build.gradle文件中。

需要注意code shrinking会减慢Gradle 编译,应避免使用它在您的调试版本中使用它。Android Studio禁用ProGuard使用 Instant Run.。

例如,以下从build.gradle文件片段,使code shrinking为发布版本

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

其中getDefaultProguardFile(‘proguard-android.txt‘)默认ProGuard设置来自于Android SDK tools/proguard/中的文件夹

更多的代码减少可以尝试使用相同位置的proguard-android-optimize.txt文件(这里我们又减少了0.5M)proguard-rules.pro是你自定义的proguard规则。

每一次build,ProGuard会输出以下文件在/build/outputs/mapping/release/:

  • dump.txt

    描述.apk文件中所有类文件间的内部结构

  • mapping.txt

    列出了原始的类,方法和字段名与混淆后代码间的映射。这个文件很重要,当你从release版本中收到一个bug报告时,可以用它来翻译被混淆的代码。

  • seeds.txt

    列出了未被混淆的类和成员

  • usage.txt

    列出了从.apk中删除的代码

  • resources.txt

    列出resource被保留的资源

自定义混淆规则

在某些情况下,默认的混淆器配置文件proguard-android.txt文件是会移除所有只有未使用的代码,但也有可能会误删除了你需要的代码,所以要注意以下几种情况:

  • AndroidManifest.xml配置的文件类
  • 使用了JNI 的接口方法
  • 运行时反射调用方法(不过现在ProGuard已经可以处理这种了)

添加-keep来忽略一下防止被混淆的代码到proguard-rules.pro文件中,比如:

-keep public class MyClass

另外也可以使用@Keep注解在你的需要忽略的代码中,需要Annotations Support Library的支持

有关自定义proguard-rules.pro文件的更多信息,可以参考ProGuard Manual.这里Troubleshooting列出了一些常见的问题。

清除无用的资源文件

Resource shrinking 只与Code shrinking 一起工作。在代码中删除所有未使用的代码后,Resource shrinking才可以识别哪些资源的应用程序仍然使用,你必须先删除未使用的代码,Resource才会成为无用的,从而被清除掉。

添加shrinkResources true属性在你的 build.gradle文件中,相应代码块如下:

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

resource shrinker 目前还不支持移除定义在values/目录下的资源文件(strings,dimensions,styles,colors),因为Android Asset Packaging Tool(AAPT)不允许Gradle Plugin指定预定义的版本资源[issue 70869]

指定要忽略的资源文件

如果您希望保留或丢弃特定的资源,请在项目中创建一个XML文件,并使用“资源”标签,并指定每个资源保存在工具中:保持属性和每个资源在工具中丢弃:丢弃属性。两个属性都接受一个逗号分隔的资源名称列表。你可以使用星号作为外卡

相应代码块如下:

<?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目录保存res/raw/keep.xml文件,build的时候该文件不会被打包到APK里面。

启用严格的检测

通常情况下,资源产品可以准确地确定资源使用。如果你使用Resources.getIdentifier()动态获取指定资源的Id,在默认情况下,这样资源具有匹配名称的格式为潜在的使用,无法去除。

例如,下面的代码将导致所有img_前缀的资源都无法去除。


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

resource shrinker 也通过搜索代码中是否包含资源名来判断是否在build的时候删除。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

在 resource 文件中指定 shrinkMode,你可以指定 Gradle 在处理该资源文件时候的方式,默认的值为 safe,你也可以将它指定为 strict(只会保留有明确引用的资源,以及处理被 tools:keeptools:discard 标注的资源)

在后面查看资源回收情况,我们会讲到,会遇到有些xml 无法被清除的问题,使用shrinkMode可以解决这个问题。

清除未使用的替代资源

Gradle resource shrinker 只删除你在代码中未使用资源,这意味着它不会删除不同的设备配置的可替代资源。如果有必要,你可以使用Android Gradle plugin 的resconfigs属性删除替代资源文件。

例如:我们项目中适配10种国家语言,而项目依赖了v7、v4等其他support包里面包含20种国家语言,那么我们可以通过resconfigs 删除剩余的可替代资源文件,这对于我们APK大小可减少了不少,

以下代码说明了如何限制你的语言资源,只是英语和法语:

android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

像上面那样通过resconfig属性指定的语言。未指定的语言的任何资源都被删除。

同样的图片资源我们也可以这么做,例如:我们提供一套xxhdpi的图片资源,其他的都过滤清除掉,这样大量清除了support,其他第三方library的资源文件,关于这个待会我们在后面会说。

合并重复的资源

默认情况下,Gradle也将同名的资源,比如相同的名字,可能是在不同的资源文件夹下,这样子不能通过shrinkResources属性来去除。

只有当两个或多个文件共享相同的资源名称、类型和限定符时才发生资源合并,关于资源的合并优先级如下:

Gradle 主要从以下位置合并资源:

  • src/main/res/ 主要资源
  • Gradle 使用variant(build type 和 build flavors)
  • Library的依赖使用

Gradle合并重复资源优先顺序为:

Dependencies → Main → Build flavor → Build type

例如:如果一个重复的资源在你的mian res中和一个Build flavor指定 ,Gradle 会优先选择Build flavor

查看资源回收

当你Gradle resource shrinker,Gradle Console 输出日志,移除APK资源的信息。例如:

:android:shrinkDebugResources
Removed unused resources: Binary resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

APK构建完成后会Gradle会生成一个resource.txt 在 /build/outputs/mapping/release/ 中,这个文件包括详细信息,如资源参考其他资源和使用或删除资源的详细信息等。

例如:找出为什么@drawable/ic_plus_anim_016,仍然包含在你的APK中,在resource.txt 搜索该文件名,你可能会发现它是被另一个资源引用,如下:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

现在需要知道为什么@drawable/add_schedule_fab_icon_anim 仍然在使用,搜索我们可以知道应该有代码引用着add_schedule_fab_icon_anim

如果你不使用严格的检查(就是上面讲的shrinkMode),同样的我们如果我们在drawable中使用了字符串’#FFFFFF‘ 这样的使用resource shrinker也不能将他移除在APK中,通常我们可以在Gradle Console中看到以下信息:

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

那么这种情况我们如何解决呢,可以通过以下方法来清理:

  • 使用tools:discard,在 tools:shrinkMode=”strict” 的时候生效,指定某资源文件需要删除。在你确定该资源文件无效的时候使用。
  • 利用Lint找出未使用的资源并清理掉

    在Android Studio中打开“Analyze” 然后选择”Inspect Code…”,范围选择整个项目,然后点击”OK”

到这里APK的大小又小了不少。

使用APK Splits构建APK

虽然我们上面很好的使用了resource shrinker可以回收一些未使用的资源(v7、v4、google Service 等Libarry资源),但有些资源仍然未被清除。

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

APK Splits比起使用 flavors,能让应用程序更有效地构建一些形式的多个apk。

多 apk 只支持以下类型:

  • 屏幕密度
  • ABI

使用新的APK Splits,构建同一个应用程序的hdpi版本和mdpi版本,能够共享很多的任务 (如 javac,dx,proguard)。此外,它会被认为是一个单一的variant,并且同一个测试程序将会被用来测试每??个多APK。

按屏幕密度拆分

android {
  ...
  splits {
    density {
      enable true
      exclude "ldpi", "tvdpi", "xxxhdpi"
      compatibleScreens ‘small‘, ‘normal‘, ‘large‘, ‘xlarge‘
    }
  }
  • enable: 启用屏幕密度拆分机制
  • exclude: 默认情况下所有屏幕密度都包括在内,你可以移除一些密度。
  • include: 表示要包括哪些屏幕密度
  • reset(): 重置屏幕密度列表为只包含一个空字符串 (这能够实现,在与include一起使用时可以表示使用哪一个屏幕密度,而不是要忽略哪一些屏幕密度)
  • compatibleScreens:表示兼容屏幕的列表。这将会注入到manifest中匹配的 节点。这个设置是可选的。

构建完成后可以在out/apk/目录下看到多个版本的APK

按 ABI 拆分

android {
  ...
  splits {
    abi {
      enable true
      reset()
      include ‘x86‘, ‘armeabi-v7a‘, ‘mips‘
      universalApk true
    }
  }
}
  • enable: 启用ABI拆分机制
  • exclude: 默认情况下所有ABI都包括在内,你可以移除一些ABI。
  • include:指明要包含哪些ABI
  • reset():重置ABI列表为只包含一个空字符串(这可以实现,在与include一起使用来可以表示要使用哪一个ABI,而不是要忽略哪一些ABI)
  • universalApk:指示是否打包一个通用版本(包含所有的ABI)。默认值为 false。

例如:我们项目主要提供xxhdpi的图片资源,而项目中引用到了很多第三方库(v7、v4、google Service 等Libarry资源)我们无法使用到,那么我们可以通过这种方法来去除那些资源。这样我们的APK又减小了非常多。

使用多版本的APK

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

通过发布有多个APK,我们可以:

目前我们基于这个方案做了不同屏幕的APK。

资源动态加载

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

依赖库优化

  • 如果应用支持的最低版本是API14,那就不要使用android support库,或者分开使用android support库,用哪个引入哪个库(android-support-ui/android-support-core 等)虽然现在支持还不太好,Google servie 也是如此。
  • 使用更轻量级的库代替,或者优化library的大小,不然自己写更好。
  • 删除armable-v7包下的so、删除x86包下的so,基本上armable的so也是兼容armable-v7的,armable-v7a的库会对图形渲染方面有很大的改进,不过最好的是根据上面我们说的提供多版本APK,对不同的平台精简,再或者动态的加载so。
  • 使用H5编写界面,图片云端获取
  • 资源缓存库不放在assets下,云端获取更新。

支持插件化

未来对于一些独立业务模块,可以做成插件化动态加载,用户需要使用时,只需下载少部分插件。

使用ReDex优化

ReDex是Facebook开源一个减小安卓app大小以提高性能的工具,内嵌以及清除僵尸代码这样的优化来减小字节码,主要是对Dex进行了优化,能让APK 运行更快,不过需要多测试是否会崩溃。

教程很简单具体更详细的内容请参考:

https://code.facebook.com/posts/998080480282805/open-sourcing-redex-making-android-apps-smaller-and-faster/

github地址:

https://github.com/facebook/redex.git

关于APK瘦身就我们就总结到这了,应该还有很多更好的方案,若有错漏,欢迎补充。

更多Android 之美,请阅读《Android 之美 从0到1 – 高手之路》系列文章

水平有限,若有错漏,欢迎指正,批评,如需转载,请注明出处–http://blog.csdn.net/vfush,谢谢!

作者:fuchenxuan

出处:http://blog.csdn.net/vfush

欢迎访问我的个人站点:http://fuchenxuan.cn

转载请注明出处–http://blog.csdn.net/vfush

时间: 2024-08-04 18:11:49

[Android 之美] 那些你不知道的APK 瘦身,让你的APK更小的相关文章

39、apk瘦身(转载)

本文转自::Android开发中文站 » 关于APK瘦身值得分享的一些经验 从APK的文件结构说起 APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用移动网络的用户来讲,消耗流量越多就代表需要花更多的钱去购买流量.同时一些第三方应用商城也会对上传的APK大小有限制,所以为了能够让产品能够更受商城和用户欢迎,APK瘦身是第一步,更小的APK标示着更多地用户愿意去下载和体验. 为了能够减小APK的大小,首先需要知道APK由哪些部分构成,然后针对每个部

关于APK瘦身值得分享的一些经验

从APK的文件结构说起 ??APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用移动网络的用户来讲,消耗流量越多就代表需要花更多的钱去购买流量.同时一些第三方应用商城也会对上传的APK大小有限制,所以为了能够让产品能够更受商城和用户欢迎,APK瘦身是第一步,更小的APK标示着更多地用户愿意去下载和体验. ??为了能够减小APK的大小,首先需要知道APK由哪些部分构成,然后针对每个部分做相应的优化工作,下图是一个APK解压后的文件结构: ??各文件的

安卓APK瘦身

之前打包的时候直接就用eclipse或者android studio直接生成签名文件,并没有关心大小问题,近期有人问我有没有对APK进行瘦身.对这方面内容一致没有关注过,今天试用了各种方式把项目签名apk降低了1/4大小.这样对用户节省了流量. 1.APK文件分析 APK是Android的安装包. APK文件事实上是zip格式,可是后缀被改动为apk,我们能够直接解压文件夹例如以下: 分析一下各个内容的作用 查看文件/文件夹大小主要针对assets.classes.dex.lib.res内容进行

Android性能优化系列之apk瘦身

Android性能优化系列之布局优化 Android性能优化系列之内存优化 为什么APK要瘦身.APK越大,在下载安装过程中,他们耗费的流量会越多,安装等待时间也会越长:对于产品本身,意味着下载转化率会越低(因为竞品中,用户有更多机会选择那个体验最好,功能最多,性能最好,包最小的),所以apk的瘦身优化也很重要,本篇博客将讲述apk瘦身的相关内容. 包体分析 在Android Studio工具栏里,打开build–>Analyze APK, 选择要分析的APK包 可以看到占用空间的主要是代码.图

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

作者:非戈@阿里聚安全 1.我是怎么思考这件事情的 APK是Android系统安装包的文件格式,关于这个话题其实是一个老生常谈的题目,不论是公司内部,还是外部网络,前人前辈已经总结出很多方法和规律.不过随着移动端技术近两年的飞速发展,一些新的思维方式和优化方法也逐渐涌现和成熟起来.笔者在实践过程中踩过一些坑,收获了一些经验,在这里做个思考和总结,所以随笔给大家,希望对大家从事相关工作的时候有所帮助和参考,同时也是抛砖引玉,希望大家共同探讨这个开放性的话题. 关于为什么APK要瘦身,这个不多说,只

apk瘦身 提高优化效果

APK瘦身记,如何实现高达53%的压缩效果 我是怎么思考这件事情的 APK是Android系统安装包的文件格式,关于这个话题其实是一个老生常谈的题目,不论是公司内部,还是外部网络,前人前辈已经总结出很多方法和规律.不过随着移动端技术近两年的飞速发展,一些新的思维方式和优化方法也逐渐涌现和成熟起来.笔者在实践过程中踩过一些坑,收获了一些经验,在这里做个思考和总结,所以随笔给大家,希望对大家从事相关工作的时候有所帮助和参考,同时也是抛砖引玉,希望大家共同探讨这个开放性的话题. 关于为什么APK要瘦身

apk 瘦身

原因: 1. 用户下载完成太慢导致新增少 2. CDN费用与包大小成正比 操作: 1.去除资源图片 无效字符串 去除不用的资源, 图片尽量小, 且非.9图片进行压缩  TinyPNG 工具进行压缩 2.代码优化 删除无用代码  用lint工具查看 3.用到的类库 armabi 下只用 x86即可 System.loadLibrary加载 或者 jar ,so 库从网络下载后 用system.load 加载 4.功能插件 部分独立功能 插件化 5.代码优化 去除无用,重复,冗余代码 android

apk瘦身

概念:在apk正常运行的情况下,功能不变,减少apk的大小 作用:apk小 可以减少流量,还可以减少安装后占用空间 掌握三个变量就可以将apk瘦身 三个变量都和as的配置文件文件有关 1. minifyEnabled jar包 如果希望apk小点 把jar包里面的类混淆下,可以减少classes.dex文件的大小 ,完成这个过程只要把这个变量?true 优化的地方1.将类名 方法名 变量名 等都换为了一个字符 2.将没有引用的过滤掉 不会被编译进apk 2. shrinkResources  整

Android APK瘦身大法——SVG图片瘦身

前两天和上家公司的上司无意聊了聊工作的事,也就顺便扯到了apk瘦身上.主要是通过SVG进行图片压缩来减少app的大小.下面我就详细介绍一下如何实现SVG的图片压缩. SVG的优点 SVG 可被非常多的工具读取和修改(比如记事本),由于使用xml格式定义,所以可以直接被当作文本文件打开,看里面的数据: SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强,SVG 图就相当于保存了关键的数据点,比如要显示一个圆,需要知道圆心和半径,那么SVG 就只保存圆心坐标和半径数据,而平常我们