I have a curious question. I have an aar library, which contains and uses native .so library. Now, I want to write another lib, which depends on that library and also has native part depending on native lib from the first lib. The dependent library uses both the native code and java wrappers from the first lib.
I wonder, is there any way, how to do this by standard gradle dependency(with copied headers files from the first lib)? Or have I to build the second lib directly from the sources?
Why I need this: We have a multiplatform lib with basic functionality, for android as aar. This lib can be used in standard android app and we use it in multiple projects, which have no another native code.
But in one app we want to write multiplatform shared app code, depending on that lib and I want to have these libs separated.
Thanks!
Unlike JAR files, AAR files offer the following functionality for Android applications: AAR files can contain Android resources and a manifest file, which allows you to bundle in shared resources like layouts and drawables in addition to Java classes and methods.
In android studio, open the Project Files view. Find the . aar file and double click, choose "arhcive" from the 'open with' list that pops up. This will open a window in android studio with all the files, including the classes, manifest, etc.
jni/libs folder is where your shared library files are built from the C/C++ sources.
NOTE: This answer is deprecated, since Android Studio on longer provide explored-aar
directory.
For a better solution to use headers from aar
, check the library androidNativeBundle
Here is a workable example basic on OpenCV
, you can do the same for your first lib
.
Package the jar
, *.so
, and exported headers
(see the file OpenCV4Android/opencv/build.gradle
in the linked project how to append the headers to aar).
You get first.aar
for example from the building of first lib
.
Add the first.aar
in your other projects when you needed.
allprojects {
repositories {
jcenter()
flatDir {
dirs '/path/to/aar'
}
}
}
// in your app's build.gradle
dependencies {
// ...
compile 'com.example:example-with-header@aar'
// ...
}
Link against your native library to the first.aar
from your native build system.
If you use CMake
, it should look like this
add_library(first
SHARED
IMPORTED)
set_target_properties(
first
PROPERTIES IMPORTED_LOCATION
../../../../build/intermediates/exploded-aar/org.example/example-debug/jni/${ANDROID_ABI}/libfirst.so
# use find command to figure out the location of the first lib before use it, not sure if it's different in different build environment
# for android studio gradle plugin latest version use
# ../../../../build/intermediates/transforms/mergeJniLibs/debug/folders/2000/1f/main/lib/${ANDROID_ABI}/libfirst.so
)
# also use find command to figure the actual location of the exported header from aar
# this might be different in your environment for different version of gradle plugin
include_directories(build/intermediates/exploded-aar/com.example/example-debug/cpp/include)
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
first
${log-lib} )
As you mentioned Gradle, I am assuming you are using the latest Android Studio. I am using 1.2.2 and found it easy to build the simple NDK projects from the many tutorials floating around, but frustratingly difficult to build an NDK project of any complexity. I will summarize what I found, but I highly suggest reading this blog and this StackOverflow.
My project is similar to yours but not exactly. The trick for me was figuring out how to make Android Studio use my Android.mk, then finding the right makefile variables. Hopefully this will help.
The stock Android Studio will completely ignore any custom
Android.mk file you create, and instead auto-generate its own.
To correct this, you must first hack the build.gradle
script for your project, located at project/app/build.gradle
.
You could probably hack the top-level build.gradle, if desired.
This is my hacked build.gradle. I build on a Windows box, so I hacked it for Windows only. Uncomment the lines if you are using OSX or Linux.
project/app/build.gradle:
//import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.sample.app"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//ENABLE CUSTOM ANDROID.MK >>
sourceSets.main.jni.srcDirs= [] //Disable automatic ndk-build.
sourceSets.main.jniLibs.srcDir 'src/main/libs'
//Call regular ndk-build script from app directory
task ndkBuild(type: Exec) {
workingDir file('src/main')
commandLine getNdkBuildCmd()
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
task cleanNative(type: Exec) {
workingDir file('src/main')
commandLine getNdkBuildCmd(), 'clean'
}
clean.dependsOn cleanNative
}
//ENABLE CUSTOM ANDROID.MK <<
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.google.android.gms:play-services:7.5.0'
}
//ENABLE CUSTOM ANDROID.MK >>
def getNdkDir() {
if (System.env.ANDROID_NDK_ROOT != null)
return System.env.ANDROID_NDK_ROOT
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkdir = properties.getProperty('ndk.dir', null)
if (ndkdir == null)
throw new GradleException("NDK location not found. Define location with ndk.dir in the local.properties file")
return (ndkdir)
}
def getNdkBuildCmd() {
def ndkbuild = getNdkDir() + "/ndk-build.cmd"
// def ndkbuild = getNdkDir() + "/ndk-build"
// if (Os.isFamily(Os.FAMILY_WINDOWS))
// ndkbuild += ".cmd"
return ndkbuild
}
//ENABLE CUSTOM ANDROID.MK <<
Now I can create my multi-library Android.mk. This builds two static-link libraries, then builds the final dynamic-link libraries and links in the first two. The directories involved are:
Directories:
project/
app/
build.gradle
src/
main/
java/
jni/
Android.mk
include/
libmp3lame/
MiniMp3/
mp3_jni.c
mpglib/
Android.mk:
TOP_PATH := $(call my-dir)
LOCAL_PATH := $(TOP_PATH)/libmp3lame
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_CFLAGS := -DSTDC_HEADERS
LOCAL_MODULE := libmp3lame
LOCAL_SRC_FILES := \
bitstream.c \
...
version.c
include $(BUILD_STATIC_LIBRARY)
MY_LOCAL_STATIC_LIBRARIES += libmp3lame
LOCAL_PATH := $(TOP_PATH)/mpglib
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_C_INCLUDES += $(TOP_PATH)/libmp3lame
LOCAL_CFLAGS := -DSTDC_HEADERS
LOCAL_MODULE := mpglib
LOCAL_SRC_FILES := \
common.c \
...
tabinit.c
include $(BUILD_STATIC_LIBRARY)
MY_LOCAL_STATIC_LIBRARIES += mpglib
LOCAL_PATH := $(TOP_PATH)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(TOP_PATH)/include
LOCAL_CFLAGS := -DSTDC_HEADERS
LOCAL_MODULE := Mp3
LOCAL_STATIC_LIBRARIES := $(MY_LOCAL_STATIC_LIBRARIES)
LOCAL_LDLIBS := -llog
LOCAL_SRC_FILES := \
./mp3_jni.c
include $(BUILD_SHARED_LIBRARY)
You will need to tweak the Android.mk.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With