Android apps are typically written in Java* because of its elegant object-oriented design and the fact that the Android Software Development Kit (Android SDK) provides cross-platform features in Java. At times, however, developers need to use Android’s native interface, especially if memory management and performance are critical issues. Android’s native interface is provided through the Native Development Kit (NDK), which supports native development in C/C++.
There are many NDK apps on the Google software market. To reduce package size, some ISVs release a single APK but not a FAT APK (a single APK contains either an ARM* or an x86 library, while a FAT APK has both). FAT APKs have advantages over single APKs. They are easier for end users to access and the libraries aren’t overlapped during application updating. Developers are, therefore, encouraged to make FAT APKs for the x86 Android software ecosystem. But how can developers mitigate large FAT APK file sizes?
Zip* vs. LZMA
To solve the FAT APK size problem, the author has developed a native library compression SDK. The basic idea uses the Lempel–Ziv–Markov chain algorithm (LZMA) (LZMA SDK (Software Development Kit)) to compress native libraries. Google uses Zip to compress all of the content, and while Zip is fast, its compression rate is lower than LZMA. LZMA is especially good for compressing the large dictionary files found in native libraries. The following graph shows the difference in file sizes after performing compressions with Zip and LZMA.
Figure 1: Size comparison of a file after being compressed with Zip* and LZMA1
Figure 2: Size comparison of multiple files (same CPU architecture) after being compressed with Zip* and LZMA1
Figure 3: Size comparison of multiple files (different CPU architecture) after being compressed with Zip* and LZMA1
Figure 4: Size comparison of three identical files after being compressed with Zip* and LZMA1
From the four charts above, we may conclude that LZMA can reduce the redundancy between the files. In the extreme (three identical files), LZMA can get a higher compression rate than Zip. This feature is particularly suitable for compressing native libraries. Generally, a native library will use the same code to get “armeabi”,”armeabi-v7a”,”x86” or even “mips” libraries, and for “armeabi-v7a” there is ARM NEON* and non-NEON code as well. These libraries have redundancies due to the same source code. Even with different architecture, for example,libffmpeg_armv7_vfpv3.so and libffmpeg_x86.so, when one is ARMEABI and the other is x86, the compression rate is 40%, while for a single file, the rate is only 20%.
Native Library Compression SDK
The native library compression SDK, the author developed, can help app developer to integrate the LZMA native library compression to obtain smaller files. The core idea of this SDK is to compress the entire native library into theasserts folder as follows.
The API in this SDK is very simple. On the first run of this application, the code extracts the entire native library from the asserts folder.
static boolean NewInstance(Context cont,Handler hdl,boolean showProgress)
static DecRawso GetInstance()
String GetPath(String libname)
It is recommended that app developers should modify the source code and add only NewInstance when the application starts, then change system.loadlibrary(***) to system.load(DecRawso . GetInstance ().GetPath(***)). After these minor changes, the APK will be smaller yet run the same as before.
If developers can ensure enough delay from the calling of NewInstance to the first loading of native library, they should call (NewInstance (cont,null,false)) without the Handler. Otherwise, a Handler should be passed to receive the “decode end” asynchronous message.
The author integrated this SDK into MoboPlayer* on an Intel® Atom™ Z2760 processor-based tablet (codenamed Clover Trail). The code calls NewInstance in the synchronization method when users start the app and the splash screen is displayed. Calling NewInsance is transparent to the end user since the decoding is done in the background. The following chart shows the compression result.
Table 5: MoboPlayer* FAT APK size compression1
Enhanced Functions of Native Library Compression SDK
Besides the LZMA compression, this SDK provides additional functions to encourage developers to include an x86 native library with the following:
- Cloud Storage: ISVs can store the x86 libraries on the cloud server, and these libraries can be downloaded from the server after installation. This action is automatically done on x86 devices and is only enabled when Wi-Fi* is connected.
- Missing Library Detection: If x86 libraries are missing, the SDK will automatically re-extract ARM libraries. An ISV can copy the ARM library to the x86 folder to avoid this issue, but it must make sure there is no cross-reference between ARM and x86 libraries.
- Java Tool for Packaging: A Java Package Tool is provided to convert normal APKs to LZMA-compressed APKs. This tool supports Windows*, Linux*, and Mac* systems. You can use it as: ComPressApk.jar -a C:/my/test.APK -k c:/key *** ### alias, if “-k” is missing (that is to say, you can just call ComPressApk.jar -a C:/my/test.APK), the Eclipse* default test key will be used to sign this APK. This tool can be integrated into an ants build script to support automatically compiling and publishing.
- Filter: The ConfigureFilter function allows you to only extract the necessary libraries. For example, enteringConfigureFilter("libffmpeg", "libffmpeg_armv7_neon.so") means only extract libffmpeg_armv7_neon.soamong all of the libraries that start with “libffmpeg”. This is useful to decrease the post-install size.
For more such Android resources and tools from Intel, please visit the Intel® Developer Zone