Building Native Android Libraries with the Latest Experimental Android Plugin
Time to read: 3 minutes
A major challenge we have faced with NDK development on our Android Video SDK is integrating with the prescribed Android Studio Gradle-based workflow. Our primary issue is debugging native components inside of an Android library. The latest updates to Android Studio and the Android Gradle Plugin indicate that Google is investing in improving the developer experience for native development on Android and ensuring that it has first class functionality in their flagship IDE. We have been closely following the progress of this new plugin. The recent addition of static library support enticed us to deep dive into migrating from the traditional Application.mk, Android.mk, and ndk-build work flow. We took the plunge and decided to share some of our findings migrating to this new build workflow.
Migration Discoveries
The new experimental plugin should look familiar for Gradle users and those familiar with the NDK build process. Most of the usual flags are available within the android.ndk
model. Google’s full documentation to the experimental plugin is available here.
We put together a sample migration process that represents some of the challenges we faced that are not mentioned in the current documentation. The sections below provide micro conversion examples to ensure you are able to recreate your .mk files in your build.gradle.
Starting Out
The NDK Samples provide a good introduction into the experimental plugin and how it can be leveraged with native development. The new plugin leverages Gradle’s new model approach. The following snippet shows the base model configuration that will be added to in the ensuing conversions.
Here are the equivalent parameters inserted into the android model. Note that the plugin will not complain if you specify 64 bit architectures and a platform version less than 21 See #201561.
Converting Android.mk
As mentioned, we designed this sample migration process to highlight the following challenges we faced:
1. Using debug builds of our native dependencies
2. Including static libraries
3. Handling dependencies on dependencies (foo.a depends on bar.a)
4. Declaring a whole static library (–whole-archive)
Here is an Android.mk file that presents each of these challenges:
Working with Static Libraries
Referencing static libraries requires adding libs entries to our repositories model. The snippet below highlights solutions for challenges one and two. Note the absence of my-whole-static-library.
Uncharted Waters
Up to this point the migration process could probably be performed just by following the NDK samples and sifting through the experimental plugin guide. Let’s revisit the remaining challenges.
- Handling dependencies on dependencies (foo.a depends on bar.a)
- Declaring a whole static library (–whole-archive)
The solutions required for these challenges are slightly more involved and here is why:
First Assumption
Let’s assume we do not need the whole-static-library
and we can just move forward with our other two static libraries, but as mentioned my-other-static-library
depends on my-static-library
. This means that if we were to try to compile, Gradle would complain about not being able to resolve symbols at link time. We could add whole-static-library
to our ldLibs
array, but there is no mechanism to specify which architecture.
Second Assumption
Let’s assume challenge three did not exist and we add whole-static-library
in the same way we have added the others. We would be able to compile, but would hit method not found exceptions if we were to call native methods from the Java layer because the compiler will strip away functions it deems not being used. We could manually add whole-static-library in the ldFlags wrapped in --whole-archive
, but again we have no way to specify which architecture.
The solutions required for challenges three and four required some research into Gradle’s incubating Rule based model configuration and an understanding of the tasks generated by the experimental plugin. If you were to run ./gradlew tasks --all in our sample you would find the following:
These tasks are dedicated to linking, and if we modify the arguments passed to the linker we can solve challenges three and four: handling dependencies on dependencies and declaring a whole static library. By creating a RuleSource, we can apply mutations to models within the Gradle configuration. Here is the snippet that presents how to modify each of these link tasks. This snippet can be added to the bottom of build.gradle:
Bonus – Creating Javadoc Task
There are a handful of posts about creating Javadocs for Android library projects, but they are not applicable to this new experimental plugin. Here is a simple snippet to add a Javadoc creation task to your project:
Retrospective
Although slightly more involved than we thought, we have found the migration to the experimental plugin much more conducive to native development on Android. The added benefits of code completion, hybrid debug mode, and Gradle’s flexibility have brought about much needed efficiency improvements to our Android SDK development. Although the plugin is marked as experimental, the stability is impressive and the integration with Android Studio 2.0 is seamless. Unfortunately, the plugin documentation is a little sparse and the ramp up can be daunting, but overall the benefits are well worth the effort.
Follow Aaron Alaniz on Twitter here @webheadz3.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.