Android NDK: A guide to deploying apps with native libraries

Libraries written in C++ and C have long been supported in Android through the Native Development Kit (NDK). Even if you haven’t directly built native code libraries, you may have included pre-built native libraries; they can be identified by the .so file extension. The main drivers for Android developers to include native libraries are:

  • Porting existing code to Android
  • Building a library of functionality
  • Increasing performance for intensive algorithms

Note: The process of building a native library is not covered in this article. Head to the Android NDK site for the tools and information you need to build native libraries for Android.

Android NDK

Most apps won’t benefit from native libraries and most apps should be developed exclusively using Java, which is executed in a virtual machine on the device using the Java ‘Write once, run anywhere’ philosophy. Including native libraries in your app results in you losing the write once, run anywhere benefit. Figure 1 shows a summary of the pros and cons of using native libraries:

Figure 1: Pros and cons of using a native library

Figure 1: Pros and cons of using a native library

Android Architectures and the Application Binary Interface (ABI)

Different Android devices use different CPU architectures, which in turn support different instruction sets. An ABI is different for each combination of CPU and instruction set. If you build with native code you need to compile that native code for each ABI that your app supports.

ABIs supported by Android

Android supports architectures from ARM, Intel and MIPS technologies, each in 32 bit and 64 bit flavors. In figure 2, below, you can see each ABI supported along with example devices.

Figure 1: Android ABI support

Figure 2: Android ABI support

Shared native library example

In this article I will use the SQLCipher project as an example use case for native libraries. SQLCipher is a great example of where using native libraries in Android is a necessity, here’s why:

  1. Performance benefits: SQLCipher has to decrypt/encrypt data, which requires very intensive processing
  2. Ease of reuse: The SQLCipher library was already developed in C/C++, using a native library makes is easier than rewriting and maintaining the library in different languages for different platforms

Performance testing

Building a shared native library can provide significant performance increases compared with implementing the same algorithm in Java. During some basic testing to compare some similar algorithms in native code compared to Java, I found the result ranged from double to a 20 times performance increase, however it should be noted these algorithms were heavy image manipulation processing. On less intensive use cases there will be little, if any, performance benefit.

Native library performance benefits

Figure 3: Native library performance benefits

APK size increase with native libraries

The table in Figure 4 shows the effect on the file size of an Android APK that include the .so libraries for SQLCipher. The universal APK in the table is built with libraries for all the tested ABIs.

Native library size increase table

Figure 4: APK size effect of including SQLCipher native library

For more information take a look at how to set your build.gradle file to build APKs that target different ABIs.

64 bit native libraries

When including native libraries you have to make a decision if you are going to ship with native libraries specifically targeting the 64 bit ABIs. Up until the start of 2015 this was an easy decision as 64 bit devices were only just hitting the market.

Note: It is not mandatory to build 64 bit libraries to support 64 bit Android devices. All 64 bit ABIs are backward compatible to support native libraries built for the equivalent 32 bit ABI. This applies to ARM as well as Intel and MIPS devices.

During 2015 sales of 64 bit Android devices could overtake those of 32 bit devices in some markets. If you provide native libraries specifically for 64 bit devices you should see a small performance increase with these new devices. Intel® have done some tests on Android performance using 64 bit compared to 32 bit and found performance is generally increased by 5-10%[1].

In summary, if you include 64 bit native libraries in you app:

  • You will still need to include 32 bit native libraries
  • You will not support any more devices than if you only include 32 bit libraries
  • You may see a small increase in performance for 64 bit devices[1]
  • You will increase the size of your APK (if building one APK with all native libraries)

Play Store Device support for different ABIs

While I have not been able to source official figures for sales figures of devices running each architecture, the breakdown is pretty clear through casual observation. ARM architectures dominate the Android device marketplace. There is a small percentage of x86 devices and virtually no MIPS devices.

When uploading an app to the Play Store feedback is shown with regards to the number of devices that are able to run your APK. Here is an example of the results:

Native Play Store device support

Figure 5: Play store reported supported devices

Notice in the table shown in Figure 5 that uploading an APK with armeabi native code will target 12 more devices than uploading an APK containing armeabi-v7a native code. The reason for this is that all devices running the armeabi-v7a are also able to execute the armeabi native code.

Multiple APK support on Google Play

It is possible to upload separate APKs to the play store for each ABI you are targeting. The best place to learn about this is by reading the Android documentation about Multiple APK support.
It should be noted that generally using multiple APKs to target different device configurations is discouraged. Here is what the official documentation has to say:

Note: You should generally use multiple APKs to support different device configurations only when your APK is too large (greater than 50MB) due to the alternative resources needed for different device configurations. Using a single APK to support different configurations is always the best practice, because it makes the path for application updates simple and clear for users (and also makes your life simpler by avoiding development and publishing complexity).

Verifying native libraries included in APK

It is possible to visually inspect the contents of any APK; remember that an APK is just a standard zip archive, so opening this archive you should find your native libraries in the folders for the ABIs you are targeting. For example, if you are building an APK that includes the SQLCipher native libraries for the x86 ABI, the file structure within the APK will look like:

lib/x86/
    libdatabase_sqlcipher.so
    libsqlcipher_android.so
    libstlport_shared.so

Uploading multiple APKs to the Developer Console

In the Google Play developer console you can set your app to have multiple APKs that target different devices while all being part of the one application. This means that all download counts and reviews for the different APKs still apply to the one listing in the Google Play store.

There are some rules the must be followed in order to have ABI specific APKs active for the same app. The developer documentation describes some of these rules as:

  • All APKs you publish for the same application must have the same package name and be signed with the same certificate key.
  • Each APK must have a different version code, specified by the android:versionCode attribute.
  • Each APK must not exactly match the configuration support of another APK.

The image below shows the Google Play website for the Android Maps app. Note how the Size for the app is listed as Varies with device. This is as a result of uploading multiple APKs for the one application.

Example of Play Store variation per device

Figure 7: Play store entry for multiple APK app

Setting the versionCode

If you decide you need to deliver multiple APKs for the one app you will need to implement a version scheme for each APK. In a previous blog I have suggested extending a scheme suggested in the Android documentation, as shown below.

versionCode format

Figure 6: Android versionCode suggested format reference

Read more about this version scheme and how to automate the generation of different versionCodes for different target ABIs in this article:

https://androidbycode.wordpress.com/2015/06/30/android-ndk-version-code-scheme-for-publishing-apks-per-architecture/

Uploading your APKs

The Google Play Developer Console allows you to upload multiple APKs for the same application through the Advanced Mode in the APK management section.

  1. Go to the Developer Console
  2. Select the APK management menu
  3. Select Switch to Advanced Mode button
  4. Select Upload new APK button for each APK that targets different ABI

Your developer console should look similar to Figure 8, below, once you have uploaded multiple APKs.

Developer Console multiple native versions

Figure 8: Developer console uploading multiple APKs

You will notice in figure 8 the warning message:

Some devices are eligible to run multiple APKs. In such a scenario, the device will receive the APK with the higher version code.

You may think each version uploaded is targeting a different ABI Native platform, so why is this message being shown? Remember that some ABIs can support code targeting more than one different ABI. In this example the armeabi-v7a devices also are backwards compatible to support armeabi, but the reverse is not true.

Conclusion

The most pertinent points that must be considered when deciding to deploy your app with native libraries are:

  • Don’t use native libraries if you don’t need to
  • Consider which ABIs you need to target
  • Consider if specific 64 bit libraries to maximize performance are worthwhile
  • Avoid producing multiple APKs for one app unless APK size is an issue

References

Multiple APK support in Android: 

http://developer.android.com/google/play/publishing/multiple-apks.html

Android apk-splits user-guide:

http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits

[1]How to Develop and Evaluate 64-bit Android Apps on Intel® x86 Platforms

https://software.intel.com/en-us/android/articles/how-to-develop-and-evaluate-64-bit-android-apps-on-intel-x86-platforms

1 Comment

Filed under Android NDK, Android Studio, Gradle

One response to “Android NDK: A guide to deploying apps with native libraries

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s