为了尽可能减⼩应⽤的⼤⼩, 咱们应该在公布版本中移除不使⽤的代码和资源。 另外还存在两个 优化⽅向能够⽤来缩减应⽤程序的占⽤空间, ⼀项是使⽤混同解决性能, 该性能会缩短应⽤的类 和成员的名称; 另⼀项是使⽤优化性能, 该性能会采⽤更踊跃的策略来进⼀步减⼩应⽤的⼤⼩ 。 本⽂将介绍如何通过APK的资源优化来加重应⽤程序的占⽤空间从⽽节俭⽤户资源。
提出问题
⾸先使⽤友盟+推出的产品U-APM来理论测试应⽤程序在不同的设施上的使⽤状况:
从图中能够看出应⽤程序在启动工夫上还存在优化空间, 下⼀步咱们将读取应⽤程序的内存分 配, 确定能从哪些⽅向⼊⼿对资源进⾏优化, 从⽽放慢启动工夫。
为了对以上⼏个指标进⾏详细分析, 咱们使⽤了Android 窗⼝上的 Memory 选项卡, 它将向我 们显示随工夫在堆上调配的数据量:
图中显示发⽣了 GC 事件, 删除了未使⽤的对象并开释了堆上的空间。
为了考察以后在堆中调配的内容, 咱们能够使⽤左侧的堆转储按钮。 这将对堆中以后调配的内容 进⾏快照, 并将其显示在 Android Studio 内的非凡报告屏幕中:
在左侧, 咱们看到堆中实例的直⽅图, 按类名分组。 对于每⼀个, 都有调配的对象数量、 这些实 例的⼤⼩ (浅层⼤⼩) 以及这些对象在内存中保留的⼤⼩。 后者通知咱们如果这些实例被开释 , 能够开释多少内存。 这个视图让咱们对应⽤程序的内存占⽤有⼀个重要的理解, 帮忙咱们辨认⼤ 型数据结构和对象关系。 这些信息能够帮忙咱们构建更⾼效的数据结构, 解开对象连接以缩小保 留的资源, 并最终尽可能地缩小资源占⽤。
随后, 咱们使⽤单个布局⽂件构建⼀个最⼩的 APK, 以计算布局⽂件的名称在 Android APK 中 呈现次数。
使⽤ Gradle 构建 Android 应⽤程序只须要⼀个AndroidManifest.xml⽂件。 咱们能够增加⼀个 虚构布局。
运⾏gradle assembleRelease将产⽣⼀个只有 2, 118 字节的公布版 APK。 咱们能够使⽤转储其 内容xxd并查找home_view字节序列。
依据此输入, 在 APK 中存在 3 次未压缩的门路和 1 次未压缩的仅名称。
zip ⽂件是⼀个⽂件条⽬列表, 后跟所有可⽤条⽬的⽬录。 每个条⽬都蕴含⽂件门路, ⽬录也是 如此。 这阐明了输入中的第⼀次呈现 (条⽬题目) 和最初⼀次呈现 (⽬录记录) 。
输入中呈现的两头两次来⾃resources.arsc⽂件外部, 该⽂件是资源排序的数据库。 它的内容是 可⻅的, 因为该⽂件在 APK 中未压缩。 运⾏aapt dump –values resources
build/outputs/apk/release/app-release-unsigned.apk显示home_view记录及其到门路的映 射:
APK 蕴含classes.dex⽂件中第五次呈现的名称。 它没有显示在xxd输入中, 因为⽂件被压缩 了。 运⾏baksmali dump <(unzip -p build/outputs/apk/release/app-release-unsigned.apk classes.dex)显示 dex ⽂件的字符串表, 其中蕴含以下条⽬
home_view:
这是⽤于将R.layout布局名称映射到唯⼀整数值的类中的字段。 顺便说⼀下, 该整数是
resources.arsc数据库的索引, ⽤于查找相干⽂件名以读取其 XML 内容。
总结⼀下咱们问题的答案, 对于每个资源⽂件, 残缺门路呈现 3 次, 名称呈现两次。
优化资源
Android Gradle 插件 4.2 引⼊了⼀个android.enable ResourceOptimizations=true标记, 它将 运⾏针对资源的优化。 这会aapt optimize在合并的资源和resources.arsc⽂件打包到 APK 之前 调⽤它们的命令。 优化仅适⽤于公布版本, ⽆论是否minifyEnabled设置为 true, 都会运⾏ 。 增加标记后, gradle.properties咱们能够使⽤漫反射来⽐较两个 APK以查看其成果。 输入很 ⻓, 所以咱们将按局部合成。
⾸先是 APK 中内容的差别。“压缩”列是 APK 内的老本,“未压缩”列是提取时的老本。
该res类别代表咱们的单个资源⽂件, 其⼤⼩降落了 28 个字节。 该arsc类别⽤于resource.arsc 自身, 显然, 这⾥产⽣了⼀定的优化。
这两局部代表资源数据库的代码和内容。 没有变动, 咱们能够推断优化没有影响
R.layout.home_view字段和home_view资源条⽬。
最初显示了优化的成果。 咱们的布局资源的⽂件名被显著截断并移出layout/⽂件夹。
在 Gradle 项⽬中, XML 的⽂件夹和⽂件名是有意义的。 ⽂件夹是资源类型, 名称对应.arsc⽂ 件中⽣成的字段和资源条⽬。 然而, 如果这些⽂件位于 APK 中, ⽂件门路就变得毫⽆意义。 资 源优化通过使名称尽可能短来进⾏优化。
输入aapt dump资源数据库也反映了⽂件更改:
APK 中门路的所有三个呈现当初都更短, 从⽽节俭了 36 字节。 尽管 36 字节是⼀个⾮常⼩的 数字, 但整个⼆进制⽂件只有 2, 118 字节。 36 字节的节俭了 1.7% 的资源。
Nick Butcher 的Plaid应⽤程序有 734 个资源⽂件。 除了数量之外, 资源⽂件的名称更具形容 性 (这是说它们更⻓的⼀种奇异⽅式) 。 home_viewPlaid 蕴含的名称是 searchback_stem_search_to_back.xml、 attrs_elastic_drag_dismiss_frame_layout、 和 designer_news_story_description.xml。
没有资源优化的构建与启⽤它的构建进⾏⽐较:
资源优化使 APK ⼤⼩节俭了 0.76%。
Uwe Trottmann 的SeriesGuide应⽤程序有 1044 个资源⽂件。 与 Plaid 不同, 它没有本机
库, 这应该会让优化成果更好。
我再次将项⽬更新到 AGP 4.2并⽐较两个版本:
在这⾥, 资源优化可能将 APK ⼤⼩缩小 2.0%!
Chris Banes 的Tivi应⽤程序有⼀个使⽤Jetpack Compose 编写的重要⼦集, 这意味着整体资 源更少。 以后构建仍蕴含 776 个资源⽂件。
通过使⽤ Compose, Tivi 曾经在使⽤最新的 AGP 4.2。 通过两个疾速构建, 咱们能够看到资源 优化的影响:
咱们再⼀次达到了 APK ⼤⼩缩减 2.0% 的水平
APK签名
APK 签名有多个版本, 如果您的版本minSdkVersion低于 24, 则须要在签名时蕴含版本V1 。 V1 签名使⽤Java 的.jar 签名标准, 该标准将每个⽂件作为⽂件中的⽂本条⽬独自签名META-
INF/MANIFEST.MF。
在为原始单布局应⽤程序创立和配置密钥库后, 转储清单⽂件unzip -c
build/outputs/apk/release/app-release.apk META-INF/MANIFEST.MF显示以下签名:

每个⽂件的残缺门路呈现, 使每个资源门路的总呈现次数达到四次。 因为较短的名称将再次导致 此⽂件蕴含更少的字节, 因而资源优化在签名中具备更⼤的影响。
__
依据材料显示, 这⼀⽅法能够节俭 1-3% 的APK ⼤⼩。 依据理论测试, 这个范畴仿佛是正确
的。 最终节俭的费⽤将取决于 APK 中资源⽂件的⼤⼩和数量。