Inhaltsverzeichnis
Einführung
Callback in JNA
Anwendung des Rückrufs
Definition des Rückrufs
Erfassung und Anwendung des Rückrufs
在多线程环境中使用callback
Heim Java javaLernprogramm So lösen Sie das Rückrufproblem in JNA bei fortgeschrittener Java-Nutzung

So lösen Sie das Rückrufproblem in JNA bei fortgeschrittener Java-Nutzung

May 05, 2023 am 11:37 AM
java jna

    Einführung

    Was ist Rückruf? Vereinfacht ausgedrückt handelt es sich bei einem Rückruf um eine Rückrufbenachrichtigung. Wenn wir bestimmte Aufgaben benachrichtigen müssen, nachdem eine Methode abgeschlossen oder ein Ereignis ausgelöst wurde, müssen wir einen Rückruf verwenden.

    Die Sprache, in der Sie Rückrufe am wahrscheinlichsten sehen, ist Javascript. Grundsätzlich ist Rückruf in Javascript überall. Um das durch Rückrufe verursachte Problem der Rückrufhölle zu lösen, wurden in ES6 speziell Versprechen eingeführt, um dieses Problem zu lösen.

    Um die Interaktion mit nativen Methoden zu erleichtern, bietet JNA auch Callback für Rückrufe an. Das Wesentliche eines Rückrufs in JNA ist ein Zeiger auf eine native Funktion. Über diesen Zeiger können Methoden in der nativen Funktion aufgerufen werden.

    Callback in JNA

    Sehen wir uns zunächst die Definition von Callback in JNA an:

    public interface Callback {
        interface UncaughtExceptionHandler {
            void uncaughtException(Callback c, Throwable e);
        }
        String METHOD_NAME = "callback";
    
        List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(
                Arrays.asList("hashCode", "equals", "toString"));
    }
    Nach dem Login kopieren

    Alle Callback-Methoden müssen diese Callback-Schnittstelle implementieren. Die Callback-Schnittstelle ist sehr einfach. Sie definiert eine Schnittstelle und zwei Eigenschaften.

    Werfen wir zunächst einen Blick auf diese Schnittstelle. Der Name der Schnittstelle ist UncaughtExceptionHandler und sie enthält eine uncaughtException-Methode. Diese Schnittstelle wird hauptsächlich zur Behandlung von Ausnahmen verwendet, die nicht im Rückrufcode von JAVA abgefangen werden.

    Beachten Sie, dass in der uncaughtException-Methode keine Ausnahmen ausgelöst werden können und alle von dieser Methode ausgelösten Ausnahmen ignoriert werden.

    METHOD_NAME Dieses Feld gibt die Methode an, die von Callback aufgerufen werden soll.

    Wenn in der Callback-Klasse nur eine öffentliche Methode definiert ist, ist diese Methode die Standard-Callback-Methode. Wenn in der Callback-Klasse mehrere öffentliche Methoden definiert sind, wird die Methode mit METHOD_NAME = „callback“ als Callback ausgewählt.

    Das letzte Attribut ist FORBIDDEN_NAMES. Gibt an, dass die Namen in dieser Liste nicht als Rückrufmethoden verwendet werden können.

    Derzeit scheint es drei Methodennamen zu geben, die nicht verwendet werden können, nämlich: „hashCode“, „equals“ und „toString“.

    Callback hat auch einen Geschwister namens DLLCallback. Werfen wir einen Blick auf die Definition von DLLCallback:

    public interface DLLCallback extends Callback {
        @java.lang.annotation.Native
        int DLL_FPTRS = 16;
    }
    Nach dem Login kopieren

    DLLCallback wird hauptsächlich für den Zugriff auf die Windows-API verwendet.

    Bei Callback-Objekten müssen wir selbst für die Freigabe der Callback-Objekte verantwortlich sein. Wenn nativer Code versucht, auf einen recycelten Rückruf zuzugreifen, kann dies zum Absturz der VM führen.

    Anwendung des Rückrufs

    Definition des Rückrufs

    Weil der Rückruf in JNA tatsächlich den Zeiger auf die Funktion in nativer Form zuordnet. Schauen Sie sich zunächst die in der Struktur definierten Funktionszeiger an:

    struct _functions {
      int (*open)(const char*,int);
      int (*close)(int);
    };
    Nach dem Login kopieren

    In dieser Struktur sind zwei Funktionszeiger definiert, mit zwei Parametern bzw. einem Parameter.

    Die entsprechende JNA-Callback-Definition lautet wie folgt:

    public class Functions extends Structure {
      public static interface OpenFunc extends Callback {
        int invoke(String name, int options);
      }
      public static interface CloseFunc extends Callback {
        int invoke(int fd);
      }
      public OpenFunc open;
      public CloseFunc close;
    }
    Nach dem Login kopieren

    Wir definieren in Structure zwei Schnittstellen, die von Callback erben, und die entsprechende Aufrufmethode ist in der entsprechenden Schnittstelle definiert.

    Dann werfen Sie einen Blick auf die spezifische Aufrufmethode:

    Functions funcs = new Functions();
    lib.init(funcs);
    int fd = funcs.open.invoke("myfile", 0);
    funcs.close.invoke(fd);
    Nach dem Login kopieren

    Darüber hinaus kann Callback auch als Rückgabewert der Funktion verwendet werden, wie unten gezeigt:

    typedef void (*sig_t)(int);
    sig_t signal(int signal, sig_t sigfunc);
    Nach dem Login kopieren

    Für diesen separaten Funktionszeiger müssen wir eine Bibliothek anpassen und definieren Sie es darin. Der entsprechende Rückruf lautet wie folgt:

    public interface CLibrary extends Library {
        public interface SignalFunction extends Callback {
            void invoke(int signal);
        }
        SignalFunction signal(int signal, SignalFunction func);
    }
    Nach dem Login kopieren

    Erfassung und Anwendung des Rückrufs

    Wenn der Rückruf in der Struktur definiert ist, kann er bei der Initialisierung der Struktur automatisch instanziiert werden, und Sie müssen dann nur noch auf die entsprechenden Eigenschaften zugreifen Struktur. Kann.

    Wenn sich die Rückrufdefinition in einer gewöhnlichen Bibliothek befindet, lautet sie wie folgt:

    public static interface TestLibrary extends Library {
            interface VoidCallback extends Callback {
                void callback();
            }
            interface ByteCallback extends Callback {
                byte callback(byte arg, byte arg2);
            }
            void callVoidCallback(VoidCallback c);
            byte callInt8Callback(ByteCallback c, byte arg, byte arg2);
        }
    Nach dem Login kopieren

    Im obigen Beispiel haben wir zwei Rückrufe in einer Bibliothek definiert, einer ist ein Rückruf ohne Rückgabewert und der andere ist ein Rückruf, der zurückgibt Byte.

    JNA stellt eine einfache Toolklasse bereit, die uns beim Abrufen von Callback hilft. Die entsprechende Methode ist CallbackReference.getCallback, wie unten gezeigt:

    Pointer p = new Pointer("MultiplyMappedCallback".hashCode());
    Callback cbV1 = CallbackReference.getCallback(TestLibrary.VoidCallback.class, p);
    Callback cbB1 = CallbackReference.getCallback(TestLibrary.ByteCallback.class, p);
    log.info("cbV1:{}",cbV1);
    log.info("cbB1:{}",cbB1);
    Nach dem Login kopieren

    Das Ausgabeergebnis lautet wie folgt:

    INFO com.flydean .CallbackUsage – cbV1:Proxy-Schnittstelle zur nativen Funktion@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$VoidCallback)
    INFO com.flydean.CallbackUsage – cbB1:Proxy-Schnittstelle zur nativen Funktion@0xffffffffc46eeefc. (com.flydean.CallbackUsage$TestLib rary$ByteCallback )

    Es ist ersichtlich, dass diese beiden Rückrufe tatsächlich Proxys für native Methoden sind. Wenn Sie sich die Implementierungslogik von getCallback im Detail ansehen:

    private static Callback getCallback(Class<?> type, Pointer p, boolean direct) {
            if (p == null) {
                return null;
            }
            if (!type.isInterface())
                throw new IllegalArgumentException("Callback type must be an interface");
            Map<Callback, CallbackReference> map = direct ? directCallbackMap : callbackMap;
            synchronized(pointerCallbackMap) {
                Reference<Callback>[] array = pointerCallbackMap.get(p);
                Callback cb = getTypeAssignableCallback(type, array);
                if (cb != null) {
                    return cb;
                }
                cb = createCallback(type, p);
                pointerCallbackMap.put(p, addCallbackToArray(cb,array));
                // No CallbackReference for this callback
                map.remove(cb);
                return cb;
            }
        }
    Nach dem Login kopieren

    Sie können sehen, dass die Implementierungslogik zunächst ermittelt, ob es sich bei dem Typ um eine Schnittstelle handelt. Wenn es sich nicht um eine Schnittstelle handelt, wird ein Fehler gemeldet. Stellen Sie dann fest, ob es sich um eine direkte Zuordnung handelt. Tatsächlich besteht die aktuelle Implementierung von JNA ausschließlich aus Schnittstellenzuordnungen. Daher besteht die nächste Logik darin, den dem Funktionszeiger entsprechenden Rückruf von pointerCallbackMap abzurufen. Suchen Sie dann den spezifischen Rückruf entsprechend dem übergebenen Typ.

    Wenn es nicht gefunden wird, erstellen Sie einen neuen Rückruf und speichern Sie schließlich den neu erstellten Rückruf in pointerCallbackMap.

    Jeder sollte beachten, dass es hier einen Schlüsselparameter namens Pointer gibt. Wenn Sie ihn tatsächlich verwenden, müssen Sie einen Zeiger auf die echte Naivitätsfunktion übergeben. Im obigen Beispiel haben wir der Einfachheit halber einen Zeiger angepasst, der keine große praktische Bedeutung hat.

    如果真的要想在JNA中调用在TestLibrary中创建的两个call方法:callVoidCallback和callInt8Callback,首先需要加载对应的Library:

    TestLibrary lib = Native.load("testlib", TestLibrary.class);
    Nach dem Login kopieren

    然后分别创建TestLibrary.VoidCallback和TestLibrary.ByteCallback的实例如下,首先看一下VoidCallback:

    final boolean[] voidCalled = { false };
            TestLibrary.VoidCallback cb1 = new TestLibrary.VoidCallback() {
                @Override
                public void callback() {
                    voidCalled[0] = true;
                }
            };
            lib.callVoidCallback(cb1);
            assertTrue("Callback not called", voidCalled[0]);
    Nach dem Login kopieren

    这里我们在callback中将voidCalled的值回写为true表示已经调用了callback方法。

    再看看带返回值的ByteCallback:

    final boolean[] int8Called = {false};
            final byte[] cbArgs = { 0, 0 };
            TestLibrary.ByteCallback cb2 = new TestLibrary.ByteCallback() {
                @Override
                public byte callback(byte arg, byte arg2) {
                    int8Called[0] = true;
                    cbArgs[0] = arg;
                    cbArgs[1] = arg2;
                    return (byte)(arg + arg2);
                }
            };
    
    final byte MAGIC = 0x11;
    byte value = lib.callInt8Callback(cb2, MAGIC, (byte)(MAGIC*2));
    Nach dem Login kopieren

    我们直接在callback方法中返回要返回的byte值即可。

    在多线程环境中使用callback

    默认情况下, callback方法是在当前的线程中执行的。如果希望callback方法是在另外的线程中执行,则可以创建一个CallbackThreadInitializer,指定daemon,detach,name,和threadGroup属性:

     final String tname = "VoidCallbackThreaded";
            ThreadGroup testGroup = new ThreadGroup("Thread group for callVoidCallbackThreaded");
            CallbackThreadInitializer init = new CallbackThreadInitializer(true, false, tname, testGroup);
    Nach dem Login kopieren

    然后创建callback的实例:

    TestLibrary.VoidCallback cb = new TestLibrary.VoidCallback() {
                @Override
                public void callback() {
                    Thread thread = Thread.currentThread();
                    daemon[0] = thread.isDaemon();
                    name[0] = thread.getName();
                    group[0] = thread.getThreadGroup();
                    t[0] = thread;
                    if (thread.isAlive()) {
                        alive[0] = true;
                    }
                    ++called[0];
                    if (THREAD_DETACH_BUG && called[0] == 2) {
                        Native.detach(true);
                    }
                }
            };
    Nach dem Login kopieren

    然后调用:

    Native.setCallbackThreadInitializer(cb, init);
    Nach dem Login kopieren

    将callback和CallbackThreadInitializer进行关联。

    最后调用callback方法即可:

    lib.callVoidCallbackThreaded(cb, 2, 2000, "callVoidCallbackThreaded", 0);
    Nach dem Login kopieren

    Das obige ist der detaillierte Inhalt vonSo lösen Sie das Rückrufproblem in JNA bei fortgeschrittener Java-Nutzung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

    Heiße KI -Werkzeuge

    Undresser.AI Undress

    Undresser.AI Undress

    KI-gestützte App zum Erstellen realistischer Aktfotos

    AI Clothes Remover

    AI Clothes Remover

    Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

    Undress AI Tool

    Undress AI Tool

    Ausziehbilder kostenlos

    Clothoff.io

    Clothoff.io

    KI-Kleiderentferner

    AI Hentai Generator

    AI Hentai Generator

    Erstellen Sie kostenlos Ai Hentai.

    Heißer Artikel

    R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Beste grafische Einstellungen
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
    WWE 2K25: Wie man alles in Myrise freischaltet
    4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

    Heiße Werkzeuge

    Notepad++7.3.1

    Notepad++7.3.1

    Einfach zu bedienender und kostenloser Code-Editor

    SublimeText3 chinesische Version

    SublimeText3 chinesische Version

    Chinesische Version, sehr einfach zu bedienen

    Senden Sie Studio 13.0.1

    Senden Sie Studio 13.0.1

    Leistungsstarke integrierte PHP-Entwicklungsumgebung

    Dreamweaver CS6

    Dreamweaver CS6

    Visuelle Webentwicklungstools

    SublimeText3 Mac-Version

    SublimeText3 Mac-Version

    Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

    Perfekte Zahl in Java Perfekte Zahl in Java Aug 30, 2024 pm 04:28 PM

    Leitfaden zur perfekten Zahl in Java. Hier besprechen wir die Definition, Wie prüft man die perfekte Zahl in Java?, Beispiele mit Code-Implementierung.

    Zufallszahlengenerator in Java Zufallszahlengenerator in Java Aug 30, 2024 pm 04:27 PM

    Leitfaden zum Zufallszahlengenerator in Java. Hier besprechen wir Funktionen in Java anhand von Beispielen und zwei verschiedene Generatoren anhand ihrer Beispiele.

    Weka in Java Weka in Java Aug 30, 2024 pm 04:28 PM

    Leitfaden für Weka in Java. Hier besprechen wir die Einführung, die Verwendung von Weka Java, die Art der Plattform und die Vorteile anhand von Beispielen.

    Smith-Nummer in Java Smith-Nummer in Java Aug 30, 2024 pm 04:28 PM

    Leitfaden zur Smith-Zahl in Java. Hier besprechen wir die Definition: Wie überprüft man die Smith-Nummer in Java? Beispiel mit Code-Implementierung.

    Fragen zum Java Spring-Interview Fragen zum Java Spring-Interview Aug 30, 2024 pm 04:29 PM

    In diesem Artikel haben wir die am häufigsten gestellten Fragen zu Java Spring-Interviews mit ihren detaillierten Antworten zusammengestellt. Damit Sie das Interview knacken können.

    Brechen oder aus Java 8 Stream foreach zurückkehren? Brechen oder aus Java 8 Stream foreach zurückkehren? Feb 07, 2025 pm 12:09 PM

    Java 8 führt die Stream -API ein und bietet eine leistungsstarke und ausdrucksstarke Möglichkeit, Datensammlungen zu verarbeiten. Eine häufige Frage bei der Verwendung von Stream lautet jedoch: Wie kann man von einem Foreach -Betrieb brechen oder zurückkehren? Herkömmliche Schleifen ermöglichen eine frühzeitige Unterbrechung oder Rückkehr, aber die Stream's foreach -Methode unterstützt diese Methode nicht direkt. In diesem Artikel werden die Gründe erläutert und alternative Methoden zur Implementierung vorzeitiger Beendigung in Strahlverarbeitungssystemen erforscht. Weitere Lektüre: Java Stream API -Verbesserungen Stream foreach verstehen Die Foreach -Methode ist ein Terminalbetrieb, der einen Vorgang für jedes Element im Stream ausführt. Seine Designabsicht ist

    Zeitstempel für Datum in Java Zeitstempel für Datum in Java Aug 30, 2024 pm 04:28 PM

    Anleitung zum TimeStamp to Date in Java. Hier diskutieren wir auch die Einführung und wie man Zeitstempel in Java in ein Datum konvertiert, zusammen mit Beispielen.

    Gestalten Sie die Zukunft: Java-Programmierung für absolute Anfänger Gestalten Sie die Zukunft: Java-Programmierung für absolute Anfänger Oct 13, 2024 pm 01:32 PM

    Java ist eine beliebte Programmiersprache, die sowohl von Anfängern als auch von erfahrenen Entwicklern erlernt werden kann. Dieses Tutorial beginnt mit grundlegenden Konzepten und geht dann weiter zu fortgeschrittenen Themen. Nach der Installation des Java Development Kit können Sie das Programmieren üben, indem Sie ein einfaches „Hello, World!“-Programm erstellen. Nachdem Sie den Code verstanden haben, verwenden Sie die Eingabeaufforderung, um das Programm zu kompilieren und auszuführen. Auf der Konsole wird „Hello, World!“ ausgegeben. Mit dem Erlernen von Java beginnt Ihre Programmierreise, und wenn Sie Ihre Kenntnisse vertiefen, können Sie komplexere Anwendungen erstellen.

    See all articles