Skip to content
Zayin Krige edited this page Sep 7, 2018 · 2 revisions

Tips for using javacpp with Android NDK with/without maven.

Created Mar 3, 2013 by noranbora

Introduction

JavaCPP already supports Android NDK with command option 'properties'. But some people may want to use more Android-way. This document explains how to use javacpp for cpp-generation and android-ndk for compilation with/without maven.

Configuration

@Platform(library = "jniHello", include = { "hello.h"})
  • jni/Android.mk
include $(CLEAR_VARS)
LOCAL_MODULE := jniHello
LOCAL_SRC_FILES := jniHello.cpp
LOCAL_C_INCLUDES := hello.h
LOCAL_LDLIBS := -llog
LOCAL_CPP_FEATURES := exceptions
  • jni/Application.mk
APP_ABI := armeabi armeabi-v7a x86 # or simply 'all' then it will generate armeabi, armeabi-v7a, x86, mips
APP_STL := gnustl_static

Build with gradle

apply plugin: 'com.android.application'
def ndkPath

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.yourpackage"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11 -frtti -fexceptions"
            }
        }
        ndk {
            // Specifies the ABI configurations of your native
            // libraries Gradle should build and package with your APK.
            abiFilters /**/ 'x86', 'x86_64', 'armeabi', 'armeabi-v7a','arm64-v8a'
        }
    }

    externalNativeBuild {
        android {
            externalNativeBuild {
                ndkPath = getNdkDirectory().toString()
            }
        }
        cmake {
            path "CMakeLists.txt"
        }
    }
    sourceSets.main {
        jniLibs.srcDir 'src/main/libs'
        jni.srcDirs = [] //disable automatic ndk-build call
    }
}

android.applicationVariants.all { variant ->
    println("")
    println 'variant.name: ' + variant.name
    def projectDir = project.projectDir.absolutePath
    def classesDir = variant.javaCompile.destinationDir
    def jniDir = file("$projectDir/src/main/cpp/").absolutePath
    println(projectDir)
    println(classesDir)
    println(jniDir)
    println("++javacpp file path:" + (configurations.javacpp.asPath))
    println(ndkPath)
    println(configurations.javacpp.asPath)
    // NOTE: The functionlity of javacpp is create jni cpp file from java code.++++++++++++++++
    // javacpp workflow:
    // 1.generate jni cpp file by javacpp-java code file.
    // 2.nkd build with android.mk
    // 3.common compile apk file.
    variant.javaCompile.doLast {
        println '===javacpp start==='
        javaexec {
            main = 'org.bytedeco.javacpp.tools.Builder'
            //NOTE: jar version must be same with the dependency jar.
            classpath = files(configurations.javacpp.asPath)
            args '-cp', "$classesDir",
                    '-properties', 'android-arm',
                    '-Dplatform.root=' + ndkPath,
                    '-Dplatform.compiler=' + ndkPath + '/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++',
                    //NOTE: include path will auto work by javacpp.jar 1.1 version.(see Android.mk)
//                    "-Dplatform.includepath=" + includePath+"/include:" +includePath+"/libs/armeabi/include",
                    "-nocompile", "idl.baidu.cpp.*",
                    "-d", "${jniDir}" // jni file destination folder.
        }
        println '===javacpp done==='
    }
}

configurations {
    javacpp
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:28.0.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'org.bytedeco:javacpp:1.4.2'
    javacpp 'org.bytedeco:javacpp:1.4.2'
}

Manual build

  1. Run javacpp

    C:\> java -jar libs\javacpp.jar -cp bin\classes -cp 'path-to-android.jar' -d jni -nocompile com.hello.package.*
    • -cp 'path-to-android.jar' is only required if static {} code in java classes has Android code such as android.util.Log...
    • -d jni, -nocompile is required so that JavaCPP generates cpp files in jni directory and ndk-build compiles them.
  2. Run ndk-build

    C:\> ndk-build
    • so files will be generated under libs\hardware_platform such as libs\armeabi, libs\x86

Build with Maven

Assume default android-related plugin is already set in pom.xml.

  • Add javacpp dependency in <dependencies>.
<dependency>
  <groupId>org.bytedeco.javacpp</groupId>
 <artifactId>javacpp</artifactId>
 <version>0.10</version>
</dependency>
  • Use latest android-maven-plugin. Old version cannot properly recognize ndk-build target. (I tested with 3.4.0 and it's ok.)
  • To use the property ${org.bytedeco.javacpp:javacpp:jar}, add 'properties' goal in <build>.
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>2.5.1</version>
  <executions>
    <execution>
      <goals>
        <goal>properties</goal>
      </goals>
    </execution>
  </executions>
</plugin>
  • To generate cpp files, add 'exec' goal with javacpp in <build>.
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>exec-maven-plugin</artifactId>
  <version>1.2.1</version>
  <executions>
    <execution>
      <id>javacpp_generate</id>
      <phase>process-classes</phase>
      <goals>
        <goal>exec</goal>
      </goals>
      <configuration>
        <executable>java</executable>
        <arguments>
          <argument>-jar</argument>  <argument>${org.bytedeco.javacpp:javacpp:jar}</argument>
          <argument>-cp</argument>   <argument>${project.build.outputDirectory}</argument>
          <argument>-d</argument>    <argument>${project.basedir}/jni</argument>
          <argument>-nocompile</argument>
          <argument>com.package.hello.*</argument>
        </arguments>
      </configuration>
    </execution>
  </executions>
</plugin>
  • To compile cpp files with ndk-build, add 'ndk-build' goal in <build>.
<plugin>
  <groupId>com.jayway.maven.plugins.android.generation2</groupId>
  <artifactId>android-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>javacpp_generate</id>
      <phase>process-classes</phase>
      <goals>
        <goal>ndk-build</goal>
      </goals>
      <configuration>
        <target>all</target>
      </configuration>
    </execution>
  </executions>
</plugin>
  • To clean generated files, add 'clean' configuration in <build>.
<plugin>
  <artifactId>maven-clean-plugin</artifactId>
  <version>2.4.1</version>
  <configuration>
    <filesets>
      <fileset>
        <directory>libs</directory>
        <excludes>
          <exclude>*.jar</exclude>
        </excludes>
      </fileset>
      <fileset>
        <directory>obj</directory>
      </fileset>
      <fileset>
        <directory>jni</directory>
        <includes>
          <include>*.cpp</include>
        </includes>
      </fileset>
    </filesets>
  </configuration>
</plugin>