How to Build *.so Library Files into AAR Bundle in Android Studio
When making third party libraries and SDKs for Android development, you could build so files, jar files, or aar files. I prefer providing aar files for distribution since aar file is a simple zip file which includes so files, jar files, and other resources.
What’s inside aar file?
- /AndroidManifest.xml (mandatory)
- /classes.jar (mandatory)
- /res/ (mandatory)
- /R.txt (mandatory)
- /assets/ (optional)
- /libs/*.jar (optional)
- /jni/
/*.so (optional) - /proguard.txt (optional)
- /lint.jar (optional)
The structure of aar is similar to apk.
How to create a basic aar file?
Click File > New > New Module and select Android Library:
Comparing build.gradle files, you will see the main difference is that the plugin name for aar is “com.android.library”, whereas the plugin name for application is “com.android.application”.
How to package prebuilt .so library files into .aar file?
The simplest way to add so files into an aar file is to create a folder jniLibs under src/main. And then copy *.so files into the folder:
src/main/jniLibs/<abi>/*.so
After building the module, you can open build/outputs/aar/*.aar to verify **jni/
How to automatically build native code into .aar file in Android Studio?
Let’s do something more complicated:
- Create a static library *.a with command line tool.
- Write JNI code and create a shared library *.so in Android Studio with the static library.
Static library libtwolib-first.a
Referring to the NDK sample two-libs, quickly create libtwolib-first.a with ndk-build.
Makefile Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libtwolib-first
LOCAL_SRC_FILES := first.c
include $(BUILD_STATIC_LIBRARY)
Header file first.h:
#ifndef FIRST_H
#define FIRST_H
extern int first(int x, int y);
#endif /* FIRST_H */
Source file first.c:
#include "first.h"
int first(int x, int y)
{
return x + y;
}
Shared library libdbr.so and barcode.aar bundle
To enable NDK integration for building JNI applications, use the new experimental plugin of Gradle instead of the original plugin.
Create a new module barcode with JNI folder and dbr.c:
#include "dbr.h"
#include <stdio.h>
#include <stdlib.h>
#include "first.h"
JNIEXPORT jint JNICALL Java_com_dynamsoft_barcode_BarcodeReader_getTestResult
(JNIEnv *env, jobject thiz)
{
return first(1, 2);
}
Copy libtwolib-first.a and first.h to the jni folder.
Create BarcodeReader.java:
package com.dynamsoft.barcode;
public class BarcodeReader {
private static final String TAG = "DBR";
public BarcodeReader() {
}
/**
* Load Dynamsoft Barcode BarcodeReader shared library.
*/
static {
System.loadLibrary("dbr");
}
//////////// native methods
public native int getTestResult();
}
Modify build.gradle:
apply plugin: 'com.android.model.library'
model {
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig.with {
minSdkVersion.apiLevel = 19
targetSdkVersion.apiLevel = 23
versionCode 1
versionName "1.0"
}
}
android.signingConfigs {
create("myconfig") {
storeFile "f:\\key\\dynamsoft.jks"
storePassword "dynamsoft"
keyAlias "dynamsoft"
keyPassword "dynamsoft"
storeType "jks"
}
}
android.ndk {
moduleName = "dbr"
ldFlags.add("-L<static library path>")
ldLibs.addAll(["twolib-first"])
}
android.buildTypes {
release {
signingConfig = $("android.signingConfigs.myconfig")
minifyEnabled = true
proguardFiles.add(file('proguard-rules.pro'))
}
}
android.productFlavors {
create ("arm8") {
ndk.abiFilters.add("arm64-v8a")
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
}
A better way to link prebuilt library:
apply plugin: 'com.android.model.library'
model {
repositories {
libs(PrebuiltLibraries) {
DynamsoftBarcodeReader {
binaries.withType(StaticLibraryBinary) {
staticLibraryFile = file("lib/libDynamsoftBarcodeReader.a")
}
}
}
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig.with {
minSdkVersion.apiLevel = 19
targetSdkVersion.apiLevel = 23
versionCode 1
versionName "1.0"
}
}
android.signingConfigs {
create("myconfig") {
storeFile "f:\\key\\dynamsoft.jks"
storePassword "dynamsoft"
keyAlias "dynamsoft"
keyPassword "dynamsoft"
storeType "jks"
}
}
android.ndk {
moduleName = "dbr"
ldLibs.addAll(["m","log","jnigraphics"])
stl "stlport_static"
}
android.sources {
main {
jni {
dependencies {
library "DynamsoftBarcodeReader" linkage "static"
}
}
}
}
android.buildTypes {
release {
signingConfig = $("android.signingConfigs.myconfig")
minifyEnabled = true
proguardFiles.add(file('proguard-rules.pro'))
}
}
android.productFlavors {
create ("arm7") {
ndk.abiFilters.add("armeabi-v7a")
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
}
Build debug and release version with Gradle:
You can download more NDK Gradle project samples from https://github.com/googlesamples/android-ndk.git
How to import .aar file to an Android project?
Create an empty Android project.
Click File > New > New Module and choose Import .JAR/.AAR Package:
Click Next to specify the file name. E.g. f:\barcode-arm8-debug.aar.
Once clicked the “Finish” button, the aar file will be packaged into your Android project as a module. Press F4 to open Project Structure, and then add the dependent module:
Build the project. The shared library *.so that built in *.aar has been added to the generated apk file. app\build\outputs\apk\app-debug.apk\lib<abi>*.so.
Run the app and check the log:
The native JNI code worked!