前言

    本文旨在介绍常见的优化APK体积大小的常用方法,截图介绍多转至别人博客,我只稍微整理。

APK构成

    在讨论如何减小apk大小之前,理解apk的结构很有必要。一个APK文件包括一个ZIP 文件,该ZIP包含app的所有文件。包括java 字节码文件,资源文件和一个包含了编译后的资源文件。APK包含以下目录:

  • META-INF/:包含了CERT.SFCERT.RSA 签名文件, 以及 MANIFEST.MFmanifest 文件.
  • assets/: 包含了app的assets,app可以通过 AssetManager 对象获取到这些资源
  • res/: 包含了那些没有被编译到 resources.arsc的资源
  • lib/: 包含了用于软件处理器的编译代码,该目录包含一个子目录,针对不同平台: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips.

一个APK也包含了下面的文件,但只有 AndroidManifest.xml 是强制性的

  • resources.arsc:
    包含了编译后的资源。该文件包含了 res/values/文件夹下的所有XML内容。打包工具抽取了XML内容,将它编译成二进制格式,并且进行了压缩。该内容包括language strings和styles,以及未直接包含在resources.arsc 文件中的内容路径。比如layout文件和图片。
  • classes.dex:
    包含可以被Dalvik/ART 识别,以dex文件格式编译后的代码
  • AndroidManifest.xml:
    包含了Android核心mainfest文件。该文件罗列了app名字,版本,访问权限,和引用的library文件。该文件采用二进制XML格式。

使用APK Analyser 分解你的 APK

    Android Studio 提供了一个有用的工具:APK Analyser。APK Analyser 将会拆解你的应用并让你知道 .apk 文件中的那个部分占据了大量空间。进入Android Studio的菜单中选择Analyze->Inspecting Code即可,让我们看一下在没有经过优化之前的截图:

Android app大小优化 apk大小优化_Android app大小优化

从 Apk Analyser 的输出来看,应用的原大小是 24.4MB。经过 Play 商店的压缩,大致是 23.3MB。

从截图中可以看出主要有 3 个文件夹占据了应用的大多数空间。

  • lib — 用来存放一些用到第三方的jar包和so文件
  • classes.dex — 这是 dex 文件,包含了所有会运行在你的 DVM 或 ART 里的字节码文件。
  • res — 这个文件夹包含了所有在 res 文件夹下的文件。大部分情况下它包含所有图片,图标和源文件,菜单文件和布局。

Android app大小优化 apk大小优化_XML_02

减小 classes.dex

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

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

Android app大小优化 apk大小优化_android_03

你可以看到现在的我的应用包含 7548 个类和 52763 个方法。这个结果是没有经过混淆的。你有两个默认的混淆文件。

  • proguard-android.txt
  • proguard-rules.pro
release {
    //Enable the proguard
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), "proguard-rules.pro"

    //...
}

    就像文件名写的那样,“proguard-android.txt”是更积极的混淆选项。我们将这个作为默认的混淆配置。你可以在 /app 目录下的 proguard-rules.pro 里添加自定义的混淆配置。

    proguard-android.txt这个默认混淆配置位置是在:sdk目录下 ~sdk\tools\proguard\proguard-android.txt

    通过设置 minifyEnabled 为 true,混淆将会移除所有未使用的方法、指令以减小 classes.dex 文件。

这是启用了 minify 之后的 APK:

Android app大小优化 apk大小优化_XML_04

减小 res

    下一大块就是 res 文件夹,它包括了所有的图片,raw 文件和 XML。你不能添加/删除/修改你的 XML,因为它们包含了你的布局。但是我们可以减小图片文件。

  • “shrinkResources” 属性将会移除所有在工程中没有用到的资源。在 build.gradle 中像下面这样启用它:
release{
  //...
  //...
  shrinkResources true
  //...
}
  • “resConfigs” 属性将会在构建过程中移除所有本地化资源。添加下面的这些代码让应用只支持中文。
defaultConfig {
    //...
    //...
    //...

    //strip other than english resources
    resConfigs "zh"
}
  • 你可以使用 webp 替代 png

    如果你在用 Android Studio 2.3,并且你的应用的最低支持版本大于 18,你可以使用 webp 替代 png。webp 图片比 png 体积更小但质量一样。而且 Android 支持 webp。所以你可以在 ImageView 中像加载其它光栅图片一样加载 webp 图片。这不需要改变你的布局。

    你可以在工程选择 drawable 和 mipmap 文件夹,右击并选择 convert to webp。这将会打开下面这样的配置弹框。

Android app大小优化 apk大小优化_android_05

    点击 ok,将会将所有 png 图片转成 webp。如果 webp 图片比 png 更大,Android Studio 将会自动跳过这个文件。

Android app大小优化 apk大小优化_XML_06

只支持特定的分辨率

Android支持非常大的设备集,包括各种屏幕密度。 在Android 4.4(API级别19)及更高版本中,框架支持各种分辨率:ldpi,mdpi,tvdpi,hdpi,xhdpi,xxhdpi和xxxhdpi。 虽然Android支持所有这些分辨率,但你不需要导出光栅化资源到每种分辨率。

如果你知道只有一小部分用户使用特定分辨率的设备,请考虑是否需要支持这些分辨率。 如果你不包括特定屏幕密度的资源,Android会自动缩放最初为其他屏幕密度设计的现有资源。

如果您的应用只需要缩放的图片,您可以通过在drawable-nodpi /中使用图片的单个版本来节省更多空间。 我们建议每个应用至少包含一个xxhdpi图片版本。


so的优化

我们在接入百度地图的时候,发现需要引入很多很多so

这些so文件占了很多大体积,如果你不加控制,所有的so都会打包到你的apk了,最后发现这些so文件尽然占了我们apk的近乎三分之一的体积。然而考虑下我们的用户,基本都是跑在手机上的(没有人跑在模拟器上),所以明显x86和x86_64的so是不需要支持,那么我们可以通过配置gradle来制定只打包某些so,依然是在defualtConfig中:


Android app大小优化 apk大小优化_XML_07



defaultConfig {
        
        ... ...

        ndk {
            //设置支持的SO库架构
            abiFilters 'arm64-v8a', 'armeabi' //, 'x86', , 'x86_64', 'arm64-v8a'
        }
    }



Android app大小优化 apk大小优化_XML_07


最后打包出来的apk真的是减少了好几个MB,这是太好了

 

当然如果有一些so是你们自己开发的,那么可以参考这个文章来参考如果用ndk开发的时候减少so本身的体积,这里就不过多介绍了

https://blog.algolia.com/android-ndk-how-to-reduce-libs-size/

对第三方库进行重新定制(重新打jar包)

开发中引入大量的第三方开发库也是一个增加apk体积的重要原因,因为你把人家的代码和资源全给包含进来了。但是想想人家的代码,并不一定全要的,是否可以只引入人家的一部分代码,而不是在build.gradle中仅仅添加一行“compile”来全部依赖呢?答案是可以得!这里举一个例子

我们开发中有一个需求是将数据通过图标的方式显示出来,这里我们站在巨人的肩膀上,使用了MPAndroidChart这个优秀的开源项目(https://github.com/PhilJay/MPAndroidChart),但是发现他们的东西太多了,我们仅仅需要使用其中一种chart。如果在build.gradle里面加一句:


dependencies {
    compile 'com.github.PhilJay:MPAndroidChart:v2.2.3'
}


这要把他们的库全给引用过来了。想到他们是开源的,代码有,所以我们仅仅把他们的我们所用到的代码给剥离出来,单独打包了一个jar包引入到我们的项目里面,就OK了,减少了大量的无用依赖代码!