Java Language
Java Native Interface
Sök…
parametrar
Parameter | detaljer |
---|---|
JNIEnv | Pekare till JNI-miljön |
jobject | Objektet som åberopade den icke- static native |
jclass | Klassen som åberopade den static native |
Anmärkningar
För att installera JNI krävs både en Java och en inbyggd kompilator. Beroende på IDE och OS krävs vissa inställningar. En guide för Eclipse kan hittas här . En fullständig handledning finns här .
Det här är stegen för att ställa in Java-C ++ -länkar i Windows:
- Sammansätt Java-källfilerna (
.java
) i klasser (.class
) medjavac
. - Skapa sidhuvudfiler (
.h
) från Java-klasserna som innehållernative
metoder medjavah
. Dessa filer "instruerar" den ursprungliga koden vilka metoder den ansvarar för att implementera. - Inkludera rubrikfilerna (
#include
) i källfilerna C ++ (.cpp
) genom att implementera denative
metoderna. - Kompilera källfilerna C ++ och skapa ett bibliotek (
.dll
). Detta bibliotek innehåller den ursprungliga kodimplementeringen. - Ange biblioteksvägen (
-Djava.library.path
) och ladda den i Java-källfilen (System.loadLibrary(...)
).
Återuppringningar (kallar Java-metoder från inbyggd kod) kräver att ange en metodbeskrivning. Om deskriptorn är fel uppstår ett körningsfel. På grund av detta är det bra att få beskrivningarna för oss, detta kan göras med javap -s
.
Ringa C ++ -metoder från Java
Statiska och medlemsmetoder i Java kan markeras som infödda för att indikera att deras implementering finns i en delad biblioteksfil. Vid körning av en inbyggd metod letar JVM efter en motsvarande funktion i laddade bibliotek (se Ladda inbyggda bibliotek ), genom att använda ett enkelt namnborttagningsschema, utför konvertering av konversationer och stackinställningar och överlåter sedan kontrollen till inbyggd kod.
Java-kod
/*** com/example/jni/JNIJava.java **/
package com.example.jni;
public class JNIJava {
static {
System.loadLibrary("libJNI_CPP");
}
// Obviously, native methods may not have a body defined in Java
public native void printString(String name);
public static native double average(int[] nums);
public static void main(final String[] args) {
JNIJava jniJava = new JNIJava();
jniJava.printString("Invoked C++ 'printString' from Java");
double d = average(new int[]{1, 2, 3, 4, 7});
System.out.println("Got result from C++ 'average': " + d);
}
}
C ++ -kod
Rubrikfiler som innehåller ursprungliga funktionsdeklarationer ska genereras med javah
verktyget på målklasser. Kör följande kommando i build-katalogen:
javah -o com_example_jni_JNIJava.hpp com.example.jni.JNIJava
... producerar följande rubrikfil ( kommentarer strippade för korthet ):
// com_example_jni_JNIJava.hpp
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> // The JNI API declarations
#ifndef _Included_com_example_jni_JNIJava
#define _Included_com_example_jni_JNIJava
#ifdef __cplusplus
extern "C" { // This is absolutely required if using a C++ compiler
#endif
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString
(JNIEnv *, jobject, jstring);
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average
(JNIEnv *, jclass, jintArray);
#ifdef __cplusplus
}
#endif
#endif
Här är ett exempel på implementering:
// com_example_jni_JNIJava.cpp
#include <iostream>
#include "com_example_jni_JNIJava.hpp"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJava_printString(JNIEnv *env, jobject jthis, jstring string) {
const char *stringInC = env->GetStringUTFChars(string, NULL);
if (NULL == stringInC)
return;
cout << stringInC << endl;
env->ReleaseStringUTFChars(string, stringInC);
}
JNIEXPORT jdouble JNICALL Java_com_example_jni_JNIJava_average(JNIEnv *env, jclass jthis, jintArray intArray) {
jint *intArrayInC = env->GetIntArrayElements(intArray, NULL);
if (NULL == intArrayInC)
return -1;
jsize length = env->GetArrayLength(intArray);
int sum = 0;
for (int i = 0; i < length; i++) {
sum += intArrayInC[i];
}
env->ReleaseIntArrayElements(intArray, intArrayInC, 0);
return (double) sum / length;
}
Produktion
Att köra exempelklassen ovan ger följande output:
Åkallade C ++ 'printString' från Java
Fick resultat från C ++ 'medelvärde': 3.4
Ringa Java-metoder från C ++ (återuppringning)
Att ringa en Java-metod från inbyggd kod är en tvåstegsprocess:
- erhålla en metodpekare med
GetMethodID
JNI-funktionen medGetMethodID
och beskrivning; - ring en av funktionerna
Call*Method
listas här .
Java-kod
/*** com.example.jni.JNIJavaCallback.java ***/
package com.example.jni;
public class JNIJavaCallback {
static {
System.loadLibrary("libJNI_CPP");
}
public static void main(String[] args) {
new JNIJavaCallback().callback();
}
public native void callback();
public static void printNum(int i) {
System.out.println("Got int from C++: " + i);
}
public void printFloat(float i) {
System.out.println("Got float from C++: " + i);
}
}
C ++ -kod
// com_example_jni_JNICppCallback.cpp
#include <iostream>
#include "com_example_jni_JNIJavaCallback.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_example_jni_JNIJavaCallback_callback(JNIEnv *env, jobject jthis) {
jclass thisClass = env->GetObjectClass(jthis);
jmethodID printFloat = env->GetMethodID(thisClass, "printFloat", "(F)V");
if (NULL == printFloat)
return;
env->CallVoidMethod(jthis, printFloat, 5.221);
jmethodID staticPrintInt = env->GetStaticMethodID(thisClass, "printNum", "(I)V");
if (NULL == staticPrintInt)
return;
env->CallVoidMethod(jthis, staticPrintInt, 17);
}
Produktion
Fick flyta från C ++: 5.221
Fick int från C ++: 17
Skaffa deskriptorn
Beskrivare (eller interna signaturer ) erhålls med hjälp av javap- programmet i den sammanställda .class
filen. Här är utgången från javap -p -s com.example.jni.JNIJavaCallback
:
Compiled from "JNIJavaCallback.java"
public class com.example.jni.JNIJavaCallback {
static {};
descriptor: ()V
public com.example.jni.JNIJavaCallback();
descriptor: ()V
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
public native void callback();
descriptor: ()V
public static void printNum(int);
descriptor: (I)V // <---- Needed
public void printFloat(float);
descriptor: (F)V // <---- Needed
}
Laddar inhemska bibliotek
Det vanliga formspråket för att ladda delade biblioteksfiler i Java är följande:
public class ClassWithNativeMethods {
static {
System.loadLibrary("Example");
}
public native void someNativeMethod(String arg);
...
Samtal till System.loadLibrary
är nästan alltid statiska så att de kan inträffa under klassbelastning, vilket säkerställer att ingen inbyggd metod kan köras innan det delade biblioteket har laddats. Följande är dock möjligt:
public class ClassWithNativeMethods {
// Call this before using any native method
public static void prepareNativeMethods() {
System.loadLibrary("Example");
}
...
Detta tillåter att skjuta upp delad bibliotekbelastning tills det behövs, men kräver extra försiktighet för att undvika java.lang.UnsatisfiedLinkError
s.
Sök efter målfil
Delade biblioteksfiler söks efter i de banor som definieras av java.library.path
, som kan åsidosättas med -Djava.library.path=
JVM-argumentet vid körning:
java -Djava.library.path=path/to/lib/:path/to/other/lib MainClassWithNativeMethods
Se upp för systemvägsavskiljare: Windows använder till exempel ;
istället för :
Observera att System.loadLibrary
löser bibliotekets filnamn på ett plattformsberoende sätt: kodavsnittet ovan förväntar sig en fil med namnet libExample.so
på Linux och Example.dll
på Windows.
Ett alternativ till System.loadLibrary
är System.load(String)
, som tar hela vägen till en delad biblioteksfil, kringgår java.library.path
:
public class ClassWithNativeMethods {
static {
System.load("/path/to/lib/libExample.so");
}
...