Integrate an Android C++ Static Library with NativeScript
One thing that I find really awesome with NativeScript is the ability to make use of the native capabilities of both iOS and Android platforms without any border. In this post, I’ll show you how to use an Android static library written in C++ into a normal NativeScript application. To do this, we’ll go through the following steps:
- Create an Android library using Android Studio
- Import the native C++ library and wrap the code we want to export into a Java class, using JNI
- Export the library to a aar file, and import it into a new NativeScript plugin
- Import the plugin into a new NativeScript application
For this tutorial, I’ll use the Superpowered audio processing library. The application will enable you to play a built-in sample track, pause it and control its tempo using a slider.
First step: Go to this link and download the complete package. The archive contains a ‘Superpowered’ directory which contains the SDK. Copy it somewhere in your machine, and note the path, as you’ll use it later on to build the Android library.
Create the Android Studio Library project
Open up Android Studio (or download it first if not already done) and create a new Project named SuperpoweredAndroidExport. Check the “Include C++ Support” option:
For the Target Android Devices options, you can |et ‘Phone and Tablet’ selected, with API 15 as the Minimum SDK. Then, select Add No Activity and finally, let ‘Toolchain Default ‘as the C++ Standard, and Finish.
Then, make sure that you have both CMake, LLDB and NDK support installed. Go to
Appearance and Behavior,
SDK Tools, select the ‘CMake’, ‘LLDB’ and ‘NDK’ checkboxes and click on Ok to install:
Next, we’ll configure the app
build.gradle file, as well as the CMake build configuration. We’ll borrow the configuration from one of the Superpowered Sample projects. Open up the app
build.gradle and update it as follow:
The first thing we did is to change the project type from application to library (apply plugin: ‘com.android.library’). The configuration relies on the SDK path that you need to set in the
local.properties file of your project. Open it and add the following line:
Notice that we also used a CMake configuration through a
CMakeLists.txt file located in
src/main/cpp. Go ahead and create this file, with the following content:
This configuration allows us to define the libraries and source files we’ll use to build the target. We use our custom library (SuperpoweredPlayer, that we’ll create in a minute), some standard ones (android, OpenSLES) and the Superpowered one.
$PATH_TO_SUPERPOWERED variable is passed by the cmake command and uses the path that we need to set in the
We’re now ready to write some native code, so let’s get to work.
Write the Native Library
We’ll keep our use case simple for this tutorial. As we said, we’ll use a built-in sample track and provide two methods: one for playing / pausing the track, and one to set its tempo in order to stretch it up and down. Create a new C++ Header file in the
src/main/cpp directory named SuperpoweredPlayer and add the following content:
Note that in the constructor, the path isn’t the one of the actual track, but the application APK. The track will be extracted using the given fileOffset and the bufferSize. This is done like this because we will include the track in the resources afterwards, which isn’t known by the library.
We also have the two methods playPause() and setTempo(), to be implemented next.
Let’s now create the implementation file,
Without going into every detail, this codes does the following: Instanciate the player with some initial values (Beat Per Minutes tempo and first beat position), open the given track file and create the component responsible for the actual audio processing. Then, we expose and implement the two main methods:
Our native implementation is done but we now have to be able to use it within our Java code. To do this, we’ll export it using JNI. Add the following lines to the end of
Notice the embedded namespace declaration in the method definitions. This basically means that the exported methods will be usable in the following class
com.bubo.superpoweredandroidexport.SuperpoweredPlayerWrapper. Before creating it, let’s import the sample track that we’ll use for our example. I’ll use one from the CrossExample sample from Superpowered, that you can download here:
Put it into a new ‘raw’ directory, inside ‘res’. Now create the Java wrapper file
SuperPoweredPlayerWrapper.java inside the
com.bubo.superpoweredandroidexport package, with the following content:
Our wrapper also needs a Context to be instantiated, and fortunately for us, it’s easily accessible with NativeScript (as we’ll see later on).
We’re now ready to export our archive. Let’s run a Gradle Sync, which should generate the .aar file in the build/outputs/aar directory:
Before moving on to the NativeScript plugin part, let’s update the AndroidManifest.xml. We need to remove the application related information, otherwise it will conflict with the ones from the final NativeScript application. We also need to declare the MODIFY_AUDIO_SETTINGS permission:
Create the NativeScript Plugin
We’re now going to wrap the Android archive into a NativeScript plugin, which we’ll include in our final application. The easiest way to do this is to use Nathan Walker’s plugin seed.
Start by creating the plugin using the seed’s repository:
Then, install TypeScript if you haven’t yet:
Then go to the newly created plugin:
And run the finishing up scripts (choose superpowered-player for the name of the plugin):
now, take the aar that we generated from Android Studio, and put it into a new directory hierarchy inside the plugin:
platforms/android/libs (you can give the aar the name you want).
We’ll now create the TypeScript implementation of the plugin. Open up the
superpowered-player.android.ts file and add the following code:
The Java class that we exposed in our aar library has to be created using its fully qualified name. We pass the android context by accessing it through the ‘application’ package. Then we expose our two main methods
setTempo() by calling the corresponding Java implementations through the
By default, the seed exports the main class contained in the iOS file (here superpowered-player.ios). This isn’t a problem if you have a symetric implementation (for both ios and Android) but in our case, we’ll only make the Android one. Let’s open the
index.d.ts file and change:
Then, update the Android type definition file to declare the methods we have implemented:
Let’s now jump right to the final step: create the NativeScript application.
Create the NativeScript Application
We’ll now create an application that will consume the plugin and display the interface allowing us to interact with the library.
Next, let’s add the plugin to the app:
Let’s now create the application logic. We’ll have a button to play / pause the track, as well a slider to control the tempo level. We want to be able to make the tempo vary from 0 to 2.0 (x2), but to make the sliding smoother, we’ll set the range from 0 to 200, and divide the value by 100 before assigning it to setTempo().
app.component.ts and add the following content:
Edit the corresponding template
app.component.html like this:
Run the application on Android and enjoy the result! you are now driving the Superpowered native C++ library directly from NativeScript:
Hope you enjoyed this article. Don’t hesitate to write a comment for any feedback / question / trouble that you have 🙂NativeScript and tagged android, C++, Native, nativescript. Bookmark the permalink.