1

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.

mbarnes
  • 455
  • 2
  • 7

1 Answers1

1

If you want to add some native libraries to your existing application in Android studio, you can do it in three standard ways. Please read this link https://stackoverflow.com/a/35369267/5475941. In this post I explained how to import your JAR files in Android studio and I explained all possible ways step by step with screenshots. I hope it helps.

Community
  • 1
  • 1
Mohammad
  • 6,024
  • 3
  • 22
  • 30
  • Thanks. The project is set up like option #2. Option #1 and gotten closer results but its manual. I think that the problem is in the new build system and the experimental plugin. I have not tried option #3. Putting the library into an archive/repository and pulling it out from there may work but I wanted to try and make a local build work. – mbarnes Feb 22 '16 at 15:54
  • You're welcome buddy, Android studio is based on Maven. In Maven most of the libraries will be added like approach 3. Advanced programmers search http://search.maven.org/ for their libraries and then add them with groupid, Artifactid, and version to their project. Try to find your library there. – Mohammad Feb 23 '16 at 18:32
  • The library is proprietary to the product so... you know... I will try our internal repository in the next few days... I had to put the library in the same module for now and get the work done. I will go back to separating the modules in a few days and let ya know how it went. It still does not help the problem where the IDE does not see the other module for breadcrumbs and other things us lazy devs insist on. I will have to fix that before I check stuff in or the shop will raise my head on a pike. Im hoping someone with experience with the experimental plugin can help. – mbarnes Feb 23 '16 at 20:17