I am attempting to add a native library to my existing application. My goal is to have the C++ library compile in it's own module in Android Studio using the plugin without manual steps (if possible). I have tried a multitude of suggestions and gotten hints that there is an issue with the build system for copying libraries between modules into the build. I have not been able to solve the problem.
Here are the relevant parts (and some) of my setup:
Android Studio: Gradle Version: 2.10
Project File Structure:
myproject
+libmodule
| +src/main
| | +java
| | | `-MyWrapper.java
| | `jni
| | `-MyJNI.cpp
| `-build.gradle
+appmodule
| +src/main
| | `java
| | `-MyClass.java
| `-build.gradle
+gradle/wrapper
| `-gradle-wrapper.properties
+-build.gradle
`-settings.gradle
myproject/settings.gradle:
include ':appmodule', ':libmodule'
myproject/build.gradle:
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.6.0-alpha9'
}
}
allprojects {
repositories {
mavenCentral()
}
}
gradle-wrapper.properties:
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
myproject/libmodule/build.gradle:
apply plugin: 'com.android.model.library'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
defaultConfig.with {
minSdkVersion.apiLevel = 15
targetSdkVersion.apiLevel = 23
}
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file("proguard-rules.txt"))
proguardFiles.add(file("proguard-android.txt"))
}
}
android.ndk {
moduleName = "MyLibrary"
stl = "gnustl_shared"
cppFlags.add("-nostdinc++")
cppFlags.add("-I\$(NDK)/sources/cxx-stl/stlport/stlport")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
cppFlags.add("-fpermissive")
ldFlags.add("-nodefaultlibs")
ldFlags.add("-Lsrc/main/jniLibs/armeabi-v7a")
ldLibs.add("c")
ldLibs.add("m")
ldLibs.add("dl")
ldLibs.add("log")
ldLibs.add("gcc")
}
android.productFlavors {
create("arm") {
ndk.abiFilters.add("armeabi")
}
create("arm7") {
ndk.abiFilters.add("armeabi-v7a")
}
create("arm8") {
ndk.abiFilters.add("arm64-v8a")
}
create("x86") {
ndk.abiFilters.add("x86")
}
create("x86-64") {
ndk.abiFilters.add("x86_64")
}
create("mips") {
ndk.abiFilters.add("mips")
}
create("mips-64") {
ndk.abiFilters.add("mips64")
}
create("all")
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:23.1.1'
}
myproject/appmodule/build.gradle:
apply plugin: 'com.android.model.application'
buildscript {
repositories {
mavenCentral()
}
dependencies {
}
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
compile project(':libmodule')
// Support Libraries
// Maven Provided Libraries
compile fileTree(dir: 'libs', include: ['*.jar'])
}
model {
android {
compileSdkVersion = 23
buildToolsVersion = '23.0.2'
defaultConfig.with {
applicationId = "com.me.myandroidapp"
minSdkVersion.apiLevel = 15
targetSdkVersion.apiLevel = 23
}
}
android.dexOptions {
jumboMode = true
}
android.packagingOptions {
//Some specific removals for an included lib from maven
}
android.lintOptions {
checkReleaseBuilds = false
abortOnError = false
}
android.buildTypes {
debug {
debuggable = true
minifyEnabled = false
buildConfigFields.with {
create() {
type = "boolean"
name = "SCREENSHOTS"
value = "true"
}
}
buildConfigFields.with {
create() {
type = "boolean"
name = "DEBUGGABLE"
value = "true"
}
}
proguardFiles.add(file("proguard-rules.txt"))
proguardFiles.add(file("proguard-android.txt"))
}
release {
debuggable = false
minifyEnabled = true
buildConfigFields.with {
create() {
type = "boolean"
name = "SCREENSHOTS"
value = "false"
}
}
buildConfigFields.with {
create() {
type = "boolean"
name = "DEBUGGABLE"
value = "false"
}
}
proguardFiles.add(file("proguard-rules.txt"))
proguardFiles.add(file("proguard-android.txt"))
}
}
android.productFlavors {
create("prod") {
applicationId = "com.me.myandroidapp"
}
create("dev") {
applicationId = "com.me.myandroidapp"
}
create("beta") {
applicationId = "com.me.myandroidapp"
}
create("staging") {
applicationId = "com.me.myandroidapp"
}
}
}
if (project.hasProperty("MyProject.properties") && new File(project.property("MyProject.properties")).exists()) {
// Get properties in the property file
Properties props = new Properties()
props.load(new FileInputStream(file(project.property("MyProject.properties"))))
model {
android {
signingConfigs {
//several configs here
}
}
android.buildTypes {
release {
signingConfig = signingConfigs.release
}
}
}
}
MyWrapper.java:
package com.me.androidwrapper;
public class MyWrapper {
private native String doItNatively(String stringinput);
public String doIt(String stringinput) {
return doItNatively(stringinput);
}
static {
System.loadLibrary("MyLibrary");
}
}
myjni.cpp:
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_me_androidwrapper_MyWrapper_doItNatively(JNIEnv *env, jobject instance, jstring stringinput_) {
// C++ happens here
}
}
MyClass.java:
// Weird behavior for IDE adding dependency module happens here:
import com.me.androidwrapper.MyWrapper;
public class MyClass {
public String makeItHappen(String inputstring) {
MyWrapper mywrapper = new myWrapper();
return mywrapper.doIt(inputstring);
}
}
There are no android.mk files in the JNI.
From a cleaned project gradle executes successfully. When I run Make Module on appmodule I get the error:
Error:(5, 37) error: package com.me.androidwrapper does not exist
...and the import statement from MyClass.java is highlighted.
As noted above in the code for MyClass.java, Android Studio offers a quick fix for including a dependency for Module "libmodule" and if I select it the IDE drops the errors. The IDE then resolves the project links correctly if I command-click through the package, class or methods.
I have followed a number of solutions on StackOverflow and in groups for the plugin. There is mention of the new experimental plug-in and build system not copying native libraries between modules in several of them and I have tried methods that set up a aar/jar module as an intermediary. I have also tried methods for manually copying the aar file into the module lib and referencing it as a file or as an aar. Most of these methods have not worked, the best result I have had is to have a successful build, but the application crashes not finding libMyLibrary.so
I have successfully tested the project by including the JNI directly into the appmodule Module using gradle 2.8 with android experimental plugin 0.4.0. This is not a very good solution as the module is complex and the C++ code is maintained separately. The 0.4.0 plugin fails as a multi-module, the library's .so does not package.
There are gaps in my knowledge of the new experimental plugin and I know it has bugs, it is incomplete and some features are still in discussion. Any assistance in tracking down my mistake, or providing a comprehensive work around to the plugin's lack of features would be greatly appreciated.