Was macht die Methode registerNatives ()?

74

Was macht die private statische Methode registerNatives()der Object-Klasse in Java ?

Hybris
quelle
Ah, natürlich hatte ich die JNI-Aspekte nicht berücksichtigt ... Vielen Dank an alle!
Hybris

Antworten:

114

Die anderen Antworten sind technisch korrekt, aber für jemanden ohne JNI-Erfahrung nicht sehr nützlich. :-)

Damit die JVM Ihre nativen Funktionen finden kann, müssen sie normalerweise auf eine bestimmte Weise benannt werden. zB wird für java.lang.Object.registerNativesdie entsprechende C-Funktion benannt Java_java_lang_Object_registerNatives. Mit registerNatives(oder besser gesagt mit der JNI-Funktion RegisterNatives) können Sie Ihre C-Funktionen beliebig benennen.

Hier ist der zugehörige C-Code (aus OpenJDK 6):

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

(Beachten Sie, dass dies Object.getClassnicht in der Liste enthalten ist. Es wird weiterhin mit dem "Standard" -Namen von aufgerufen Java_java_lang_Object_getClass.) Für die aufgelisteten Funktionen sind die zugehörigen C-Funktionen wie in dieser Tabelle aufgeführt. Dies ist einfacher als das Schreiben einer Reihe von Weiterleitungsfunktionen.

Das Registrieren nativer Funktionen ist auch nützlich, wenn Sie Java in Ihr C-Programm einbetten und eine Verknüpfung zu Funktionen in der Anwendung selbst herstellen möchten (im Gegensatz zu einer gemeinsam genutzten Bibliothek) oder wenn die verwendeten Funktionen nicht anderweitig "exportiert" werden, da diese nicht "exportiert" werden würde normalerweise nicht vom Standard-Methodensuchmechanismus gefunden werden. Das Registrieren nativer Funktionen kann auch verwendet werden, um eine native Methode an eine andere C-Funktion "neu zu binden" (nützlich, wenn Ihr Programm beispielsweise das dynamische Laden und Entladen von Modulen unterstützt).

Ich ermutige alle, das JNI-Buch zu lesen , in dem es um dieses und vieles mehr geht. :-)

Chris Jester-Young
quelle
1
Sie haben einen vollständig dekorierten JNI-Funktionsnamen geschrieben, was ihn verwirrend macht: Java_java_lang_Object_registerNativesWird von VM automatisch aufgerufen, oder c / c ++ - Code muss diese Funktion aufrufen, und es besteht keine Notwendigkeit für all das Java-Gobblygook
Pavel P
@Pavel Standardmäßig wird eine native Methode methodNamein der Klasse fully.qualified.ClassNameunter dem C-Namen gesucht Java_fully_qualified_ClassName_methodName(den Ihr C- seitiger Code exportieren muss). Sie können die Methode aufrufen JNIEnv, RegisterNativesum diese Verknüpfung zu überschreiben. Mit anderen Worten, wenn Sie nicht RegisterNativeszuerst verwenden, ist "all das Java-Gobblygook" erforderlich.
Chris Jester-Young
Ah, ok, also durch Exportieren von nur 1 Funktion Java_java_lang_Object_registerNativeskann ich tatsächlich alle meine JNI-Sachen verknüpfen , indem ich den Wert von überprüfe cls. Ich habe den Teil verpasst, dass registerNatives tatsächlich Teil von Java selbst ist. Also ... wenn eine Klasse verwendet werden soll, wie sieht die Reihenfolge der Aktionen aus, mit denen JVM Eingeborene verknüpft? Es scheint, dass JVM, wenn die Eingeborenen noch nicht registriert sind (aus c / c ++), nach all diesen exportierten Namen sucht. Wenn sie nicht vorhanden sind, versucht sie, Object.registerNatives zu verwenden (oder umgekehrt?). Der jni book Link ist übrigens tot.
Pavel P
Wenn Sie nicht aufgerufen RegisterNativeshaben und die exportierten Namen nicht verfügbar sind, wird beim Versuch, die Methode aufzurufen, ein Laufzeitfehler angezeigt. Object.registerNativesist eine interne Methode der OpenJDK-Implementierung von java.lang.Object, die vom Klasseninitialisierer aufgerufen wird. Ihr einziger Zweck besteht darin, die nativen Methoden zu registrieren, die nur diese betreffen java.lang.Object. Wenn Sie Ihre eigene native Bibliothek schreiben, liegt es an Ihnen, die nativen Methoden Ihrer Bibliothek selbst zu registrieren (falls Sie dies wünschen).
Chris Jester-Young
11

Was etwas verwirrend sein kann, ist, dass der java.lang.Object.registerNativesin einer vorherigen Antwort gezeigte Code nur ein Beispiel für die Registrierung nativer Funktionen ist. Dies ist der Code, der (in der Implementierung von OpenJDK) native Funktionen für die Klasse Object registriert. Um native Funktionen für Ihre eigene Klasse zu registrieren, müssen Sie die JNI-Funktion RegisterNativesaus dem nativen Code in Ihrer eigenen Bibliothek aufrufen . Das klingt vielleicht etwas kreisförmig, aber es gibt verschiedene Möglichkeiten, die Schleife zu durchbrechen.

  1. Folgen Sie dem Beispiel dieser Implementierung der Klasse Object:

    ein. Deklarieren Sie in Ihrer Java-Klasse eine native Methode (vorzugsweise statisch) mit dem Namen registerNatives(oder einen anderen Namen. Es spielt keine Rolle).

    b. Definieren Sie in Ihrem nativen Code eine Funktion mit dem Namen Java_<your fully qualified class name>_registerNatives, die einen Aufruf der JNI-Funktion enthält RegisterNatives.

    c. Stellen Sie sicher, dass in Ihrem Java-Code Ihre Java- registerNativesMethode aufgerufen wird, bevor Sie andere native Methoden aufrufen.

ODER

  1. Verwenden JNI_OnLoad

    ein. Definieren Sie in Ihrer nativen Bibliothek eine Funktion jint JNI_OnLoad(JavaVM *vm, void *reserved). Rufen Sie im Hauptteil dieser Funktion die JNI-Funktion auf RegisterNatives.

    b. Die Java-VM sucht und ruft automatisch auf, JNI_OnLoadwenn Ihre native Bibliothek von geladen wird System.loadLibrary, die Sie bereits aufrufen sollten, wahrscheinlich in einem statischen Initialisierer für Ihre Klasse. (Sie erhalten den erforderlichen envZeiger, indem Sie die GetEnvFunktion in der Tabelle aufrufen, auf die der vmZeiger zeigt.)

Eric
quelle