-
Notifications
You must be signed in to change notification settings - Fork 6
Integrating JniGen
gershnik edited this page Feb 6, 2021
·
14 revisions
On the most basic level to use JniGen you need to
- Configure its annotation processor to run. This can be either during Java compilation or separately but must happen before C++ compilation
- Use its annotations in your Java code
- Include generated headers in your C++ code
-
Repository:
JCenterUpdate: JCenter is closing down on May 1, 2021. Use the following custom repository instead:
https://github.com/smartsheet-mobile/smjni/raw/maven
-
Annotation processor
Group Id:smjni.jnigen
Artifact Id:processor
Version: corresponds to releases on GitHub -
Annotations
Group Id:smjni.jnigen
Artifact Id:annotations
Version: corresponds to releases on GitHub
The instructions below are for Gradle. If you use something else to build your Java code you will need to figure out the equivalent steps.
repositories {
google()
mavenCentral()
maven { url "https://github.com/smartsheet-mobile/smjni/raw/maven" }
}
dependencies {
//JNI annotations
compileOnly("smjni.jnigen:annotations:3.0")
//JNI code generator
annotationProcessor("smjni.jnigen:processor:3.0@jar")
}
//Where to put the generated files
//Make sure there is nothing else in that folder (it shouldn't even exist).
//This will allow removal of stale files
def JNIGEN_GENERATED_PATH = "src/main/cpp/generated"
def JNIGEN_OUTPUT_LIST_NAME = "outputs.txt"
android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [
"smjni.jnigen.dest.path" : file(JNIGEN_GENERATED_PATH).path,
"smjni.jnigen.output.list.name": JNIGEN_OUTPUT_LIST_NAME,
"smjni.jnigen.expose.extra": [
//Put here names of all the system classes your JNI code needs access to
"android.graphics.Bitmap"
].join(";").toString()
]
}
}
}
//Use libraryVariants if you are building a library
android.applicationVariants.all { variant ->
variant.javaCompileProvider.get().outputs.upToDateWhen {
//This makes Gradle rebuild Java compilation (and so run annotation processor)
//when any of the generated files are missing
def outputList = file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME")
if (!outputList.exists()) {
return false
}
def eachPresent = true
outputList.eachLine { line ->
if (!file("$JNIGEN_GENERATED_PATH/$line").exists()) {
eachPresent = false
}
}
if (!eachPresent)
return false
return true
}
}
//This task will remove any stale headers (left from classes that are no longer exposed)
//Without it your C++ code can continue to include them with various bad results
task cleanStaleJNIHeaders {
inputs.files file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME")
doLast {
def generated = new HashSet<String>()
file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME").eachLine { line ->
generated.add(line)
}
def existing = project.fileTree(dir: file("$JNIGEN_GENERATED_PATH"), include: "*.h").files
def toDelete = []
for (file in existing) {
if (!generated.contains(file.name))
toDelete.add(file)
}
project.delete { delete toDelete }
}
}
tasks.whenTaskAdded { theTask ->
//Making cleanStaleJNIHeaders run before native build
if (theTask.name.startsWith("externalNativeBuild")) {
theTask.dependsOn cleanStaleJNIHeaders
}
}
repositories {
mavenCentral()
maven { url "https://github.com/smartsheet-mobile/smjni/raw/maven" }
}
dependencies {
compileOnly 'smjni.jnigen:annotations:3.0'
annotationProcessor("smjni.jnigen:processor:3.0@jar") {
transitive true
}
}
//Where to put the generated files
//Make sure there is nothing else in that folder (it shouldn't even exist).
//This will allow removal of stale files
def JNIGEN_GENERATED_PATH = "src/main/cpp/generated"
def JNIGEN_OUTPUT_LIST_NAME = "outputs.txt"
tasks.withType(JavaCompile) {
options.compilerArgs = [
"-Asmjni.jnigen.dest.path=" + file(JNIGEN_GENERATED_PATH).path,
"-Asmjni.jnigen.output.list.name=" + JNIGEN_OUTPUT_LIST_NAME,
"-Asmjni.jnigen.expose.extra=" + [
//Put here names of all the system classes your JNI code needs access to
"java.lang.AssertionError"
].join(";").toString()
]
outputs.file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME")
}
//This task will remove any stale headers (left from classes that are no longer exposed)
//Without it your C++ code can continue to include them with various bad results
task cleanStaleJNIHeaders(dependsOn: compileJava) {
inputs.files file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME")
doLast {
def generated = new HashSet<String>()
def outputs = file("$JNIGEN_GENERATED_PATH/$JNIGEN_OUTPUT_LIST_NAME")
if (outputs.exists()) {
outputs.eachLine { line ->
generated.add(line)
}
}
def existing = project.fileTree(dir: file("$JNIGEN_GENERATED_PATH"), include: "*.h").files
def toDelete = []
for (file in existing) {
if (!generated.contains(file.name))
toDelete.add(file)
}
project.delete { delete toDelete }
}
}
task buildNative(dependsOn: cleanStaleJNIHeaders) {
... build native code in whatever way you do it ...
}
- Building
-
User's Guide
Declaring Java Types
Accessing Methods and Fields
Representing Java Classes
Implementing Native Methods
Smart References
Error Handling
Obtaining JNIEnv
Initialization
Strings
Arrays
Direct Buffers
Booleans
Sizes -
JniGen Code Generator
Integrating JniGen
Annotations
Processor Options