为了尽可能减⼩应⽤的⼤⼩,我们应该在发布版本中移除不使⽤的代码和资源。 另外还存在两个 优化⽅向可以⽤来缩减应⽤程序的占⽤空间,⼀项是使⽤混淆处理功能,该功能会缩短应⽤的类 和成员的名称;另⼀项是使⽤优化功能,该功能会采⽤更积极的策略来进⼀步减⼩应⽤的⼤⼩。本⽂将介绍如何通过APK的资源优化来减轻应⽤程序的占⽤空间从⽽节省⽤户资源。
提出问题
⾸先使⽤友盟+推出的产品U-APM来实际测试应⽤程序在不同的设备上的使⽤情况:
从图中可以看出应⽤程序在启动时间上还存在优化空间,下⼀步我们将读取应⽤程序的内存分配,确定能从哪些⽅向⼊⼿对资源进⾏优化,从⽽加快启动时间。
为了对以上⼏个指标进⾏详细分析,我们使⽤了Android 窗⼝上的 Memory 选项卡, 它将向我 们显示随时间在堆上分配的数据量:
图中显示发⽣了 GC 事件, 删除了未使⽤的对象并释放了堆上的空间。
为了调查当前在堆中分配的内容, 我们可以使⽤左侧的堆转储按钮。 这将对堆中当前分配的内容 进⾏快照, 并将其显示在 Android Studio 内的特殊报告屏幕中:
在左侧,我们看到堆中实例的直⽅图,按类名分组。对于每⼀个,都有分配的对象数量、 这些实例的⼤⼩(浅层⼤⼩)以及这些对象在内存中保留的⼤⼩。后者告诉我们如果这些实例被释放 ,可以释放多少内存。这个视图让我们对应⽤程序的内存占⽤有⼀个重要的了解,帮助我们识别⼤ 型数据结构和对象关系。这些信息可以帮助我们构建更⾼效的数据结构, 解开对象连接以减少保 留的资源,并最终尽可能地减少资源占⽤。
随后,我们使⽤单个布局⽂件构建⼀个最⼩的 APK
,以计算布局⽂件的名称在 Android APK
中出现次数。
使⽤ Gradle
构建 Android 应⽤程序只需要⼀个AndroidManifest.xml
⽂件。 我们可以添加⼀个 虚拟布局。
.
├── build.gradle
└── src
└── main
├── AndroidManifest.xml
└── res
└── layout
└── home_view.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
记录及其到路径的映射:
Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=com.example
Package 0 id=0x7f name=com.example
type 0 configCount=1 entryCount=1
spec resource 0x7f010000 com.example:layout/home_view: flags=0x0000000
config (default):
resource 0x7f010000 com.example:layout/home_view: t=0x03 d=0x0000000
(string8) "res/layout/home_view.xml"
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
资源数据库也反映了⽂件更改:
Package Groups (1)
Package Group 0 id=0x7f packageCount=1 name=com.example
Package 0 id=0x7f name=com.example
type 0 configCount=1 entryCount=1
spec resource 0x7f010000 com.example:layout/home_view: flags=0x0000000
config (default):
resource 0x7f010000 com.example:layout/home_view: t=0x03 d=0x0000000
(string8) "res/eA.xml"
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 -cbuild/outputs/apk/release/app-release.apk META-INF/MANIFEST.MF
显示以下签名:
Manifest-Version: 1.0
Built-By: Signflinger
Created-By: Android Gradle 4.2.0-alpha08
Name: AndroidManifest.xml
SHA-256-Digest: HdoGVd8U3Zjtf2VkGLExAPCQ1fq+kNL8eHKjVQXGI60=
Name: classes.dex
SHA-256-Digest: BVA1ApPvECg56DrrNPgD3jgv1edcM8VKYjcJEAG4G44=
Name: res/eA.xml
SHA-256-Digest: nDn7UQex2OWB3/AT054UvSAx9pYNSWwERCLfgdM6J6c=
Name: resources.arsc
SHA-256-Digest: 6w7i2Z9+LjwqlXS7YhhjzP/XhgvJF3PUuyJM60t0Qbw=
每个⽂件的完整路径出现,使每个资源路径的总出现次数达到四次。由于较短的名称将再次导致 此⽂件包含更少的字节, 因此资源优化在签名中具有更⼤的影响。
根据资料显示, 这⼀⽅法可以节省 1-3% 的APK ⼤⼩。 根据实际测试 这个范围似乎是正确的。最终节省的费⽤将取决于 APK 中资源⽂件的⼤⼩和数量。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。