适用于 Android* 应用的原生库压缩 SDK






4.20/5 (2投票s)
为了解决 FAT APK 体积过大的问题,作者开发了一个原生库压缩 SDK。
引言
Android 应用通常使用 Java* 编写,因为其优雅的面向对象设计,以及 Android 软件开发工具包 (Android SDK) 提供了跨平台 Java 功能。然而,有时开发者需要使用 Android 的原生接口,特别是当内存管理和性能是关键问题时。Android 的原生接口通过原生开发工具包 (NDK) 提供,它支持 C/C++ 的原生开发。
Google 软件市场上有很多 NDK 应用。为了减小安装包大小,一些 ISV 会发布单个 APK 而不是 FAT APK(单个 APK 包含 ARM* 或 x86 库,而 FAT APK 包含两者)。FAT APK 相对于单个 APK 有优势。它们更容易被最终用户访问,并且在应用程序更新期间库不会重叠。因此,鼓励开发者为 x86 Android 软件生态系统制作 FAT APK。但是,开发者如何才能减小 FAT APK 的庞大文件大小呢?
Zip* 与 LZMA
为了解决 FAT APK 体积过大的问题,作者开发了一个原生库压缩 SDK。基本思路是使用 **Lempel–Ziv–Markov chain algorithm** (**LZMA**)(http://www.7-zip.org/sdk.html)来压缩原生库。Google 使用 Zip 压缩所有内容,虽然 Zip 速度很快,但其压缩率低于 LZMA。LZMA 特别适合压缩原生库中常见的大型字典文件。下图显示了使用 Zip 和 LZMA 进行压缩后文件大小的差异。
从以上四张图表,我们可以得出结论,LZMA 可以减少文件之间的冗余。在极端情况下(三个相同的文件),LZMA 可以获得比 Zip 更高的压缩率。这一特性特别适合压缩原生库。通常,原生库会使用相同的代码来生成“**armeabi**”、“**armeabi-v7a**”、“**x86**”甚至“**mips**”库,而“**armeabi-v7a**”还包含 ARM NEON* 和非 NEON 代码。由于源代码相同,这些库存在冗余。即使是不同架构,例如 **libffmpeg_armv7_vfpv3.so** 和 **libffmpeg_x86.so**,当一个为 ARMEABI 而另一个为 x86 时,压缩率为 40%,而单个文件时,压缩率仅为 20%。
原生库压缩 SDK
作者开发的这个原生库压缩 SDK 可以帮助应用开发者集成 LZMA 原生库压缩,从而获得更小的文件。该 SDK 的核心思想是将整个原生库压缩到 **assets** 文件夹中,如下所示。
这个 SDK 中的 API 非常简单。应用程序首次运行时,代码会从 assets 文件夹中提取整个原生库。
static boolean NewInstance(Context cont,Handler hdl,boolean showProgress) static DecRawso GetInstance() String GetPath(String libname)
建议应用开发者修改源代码,在应用程序启动时仅添加 `NewInstance`,然后将 `system.loadlibrary(***)` 更改为 `system.load(DecRawso.GetInstance().GetPath(***))`。进行这些小改动后,APK 会更小,但运行效果与之前相同。
如果开发者能够确保从调用 `NewInstance` 到首次加载原生库之间有足够的延迟,他们可以调用 `(NewInstance(cont, null, false))` 而不带 `Handler`。否则,应传递一个 `Handler` 以接收“**decode end**”异步消息。
作者已将此 SDK 集成到 MoboPlayer* 中,运行于基于 Intel® Atom™ Z2760 处理器的平板电脑(代号为 Clover Trail)。代码在用户启动应用时,在显示闪屏页面的同步方法中调用 `NewInstance`。调用 NewInstance 对最终用户来说是透明的,因为解码是在后台完成的。下表显示了压缩结果。
原生库压缩 SDK 的增强功能
除了 LZMA 压缩,该 SDK 还提供额外的功能,鼓励开发者包含 x86 原生库,方法如下:
- 云存储: ISV 可以将 x86 库存储在云服务器上,并在安装后从服务器下载这些库。此操作会在 x86 设备上自动进行,并且仅在连接 Wi-Fi* 时启用。
- 缺失库检测: 如果 x86 库缺失,SDK 将自动重新提取 ARM 库。ISV 可以将 ARM 库复制到 x86 文件夹以避免此问题,但必须确保 ARM 和 x86 库之间没有交叉引用。
- 用于打包的 Java 工具: 提供了一个 Java Package Tool,可将普通 APK 转换为 LZMA 压缩的 APK。此工具支持 Windows*、Linux* 和 Mac* 系统。您可以使用如下方式调用:**ComPressApk.jar -a C:/my/test.APK -k c:/key *** ### alias**,如果缺少“-k”(也就是说,您只需调用 **ComPressApk.jar -a C:/my/test.APK**),则会使用 Eclipse* 默认测试密钥对此 APK 进行签名。此工具可以集成到 **ants** 构建脚本中,以支持自动编译和发布。
- 过滤器: `ConfigureFilter` 函数允许您仅提取必要的库。例如,输入 **ConfigureFilter("libffmpeg", "libffmpeg_armv7_neon.so")** 意味着在所有以“**libffmpeg**”开头的库中,仅提取 **libffmpeg_armv7_neon.so**。这对于减小安装后的大小非常有用。
结论
将原生库压缩 SDK 用于您的 Android 应用可以显著减小原生库的安装包大小,并使拥有大型原生库的应用程序(通常是视频播放器、浏览器和 3D 游戏)受益。有关源代码和技术支持,请联系作者。
关于作者
Yuming Li (Yuming.li@intel.com) 是 Intel 软件与服务部门的一名软件应用工程师。他目前专注于多媒体相关应用的赋能和性能优化,特别是在 Android 移动平台上的应用。
1结果基于内部 Intel® 分析估算,仅供参考。系统硬件或软件设计或配置的任何差异都可能影响实际性能。