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 ) med javac .
  • Skapa sidhuvudfiler ( .h ) från Java-klasserna som innehåller native metoder med javah . 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 de native 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:

  1. erhålla en metodpekare med GetMethodID JNI-funktionen med GetMethodID och beskrivning;
  2. 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");
    }

    ...


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow