Table des matières
Introduction
Callback dans JNA
Application du rappel
Définition du rappel
Acquisition et application du rappel
在多线程环境中使用callback
Maison Java javaDidacticiel Comment résoudre le problème de rappel dans JNA lors d'une utilisation avancée de Java

Comment résoudre le problème de rappel dans JNA lors d'une utilisation avancée de Java

May 05, 2023 am 11:37 AM
java jna

    Introduction

    Qu'est-ce que le rappel ? Pour faire simple, le rappel est une notification de rappel. Lorsque nous devons notifier certaines tâches après qu'une méthode est terminée ou qu'un événement est déclenché, nous devons utiliser le rappel.

    La langue dans laquelle vous êtes le plus susceptible de voir le rappel est javascript. Fondamentalement, en javascript, le rappel est partout. Afin de résoudre le problème de l'enfer des rappels provoqué par les rappels, des promesses ont été spécialement introduites dans ES6 pour résoudre ce problème.

    Afin de faciliter l'interaction avec les méthodes natives, JNA propose également Callback pour le rappel. L'essence d'un rappel dans JNA est un pointeur vers une fonction native. Grâce à ce pointeur, les méthodes de la fonction native peuvent être appelées.

    Callback dans JNA

    Regardons d'abord la définition du Callback dans JNA :

    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"));
    }
    Copier après la connexion

    Toutes les méthodes de Callback doivent implémenter cette interface de Callback. L'interface Callback est très simple. Elle définit une interface et deux propriétés.

    Regardons d'abord cette interface. Le nom de l'interface est UncaughtExceptionHandler, et elle contient une méthode uncaughtException. Cette interface est principalement utilisée pour gérer les exceptions qui ne sont pas capturées dans le code de rappel de JAVA.

    Notez que dans la méthode uncaughtException, les exceptions ne peuvent pas être levées et toutes les exceptions levées à partir de cette méthode seront ignorées.

    METHOD_NAME Ce champ spécifie la méthode à appeler par Callback.

    S'il n'y a qu'une seule méthode publique définie dans la classe Callback, alors la méthode de rappel par défaut est cette méthode. Si plusieurs méthodes publiques sont définies dans la classe Callback, la méthode avec METHOD_NAME = "callback" sera sélectionnée comme rappel.

    Le dernier attribut est FORBIDDEN_NAMES. Indique que les noms de cette liste ne peuvent pas être utilisés comme méthodes de rappel.

    Actuellement, il semble qu'il existe trois noms de méthodes qui ne peuvent pas être utilisés, à savoir : "hashCode", "equals" et "toString".

    Callback a également un frère appelé DLLCallback. Jetons un coup d'œil à la définition de DLLCallback :

    public interface DLLCallback extends Callback {
        @java.lang.annotation.Native
        int DLL_FPTRS = 16;
    }
    Copier après la connexion

    DLLCallback est principalement utilisé pour accéder à l'API Windows.

    Pour les objets de rappel, nous devons être responsables de la libération des objets de rappel nous-mêmes. Si le code natif tente d’accéder à un rappel recyclé, cela peut provoquer le crash de la VM.

    Application du rappel

    Définition du rappel

    Parce que le rappel dans JNA mappe en fait le pointeur vers la fonction en natif. Tout d'abord, jetez un œil aux pointeurs de fonction définis dans la structure :

    struct _functions {
      int (*open)(const char*,int);
      int (*close)(int);
    };
    Copier après la connexion

    Dans cette structure, deux pointeurs de fonction sont définis, avec respectivement deux paramètres et un paramètre.

    La définition de rappel JNA correspondante est la suivante :

    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;
    }
    Copier après la connexion

    Nous définissons deux interfaces dans Structure qui héritent de Callback, et la méthode d'invocation correspondante est définie dans l'interface correspondante.

    Ensuite, jetez un œil à la méthode d'appel spécifique :

    Functions funcs = new Functions();
    lib.init(funcs);
    int fd = funcs.open.invoke("myfile", 0);
    funcs.close.invoke(fd);
    Copier après la connexion

    De plus, Callback peut également être utilisé comme valeur de retour de la fonction, comme indiqué ci-dessous :

    typedef void (*sig_t)(int);
    sig_t signal(int signal, sig_t sigfunc);
    Copier après la connexion

    Pour ce pointeur de fonction distinct, nous devons personnaliser une bibliothèque et définissez-le dedans Le rappel correspondant est le suivant :

    public interface CLibrary extends Library {
        public interface SignalFunction extends Callback {
            void invoke(int signal);
        }
        SignalFunction signal(int signal, SignalFunction func);
    }
    Copier après la connexion

    Acquisition et application du rappel

    Si le rappel est défini dans Structure, il peut être automatiquement instancié lorsque la Structure est initialisée, et il vous suffit alors d'accéder aux propriétés correspondantes depuis Structure.

    Si le rappel est défini dans une bibliothèque ordinaire, il est le suivant :

    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);
        }
    Copier après la connexion

    Dans l'exemple ci-dessus, nous avons défini deux rappels dans une bibliothèque, l'un est un rappel sans valeur de retour et l'autre est un rappel qui renvoie octet .

    JNA fournit une classe d'outils simple pour nous aider à obtenir le rappel. Cette classe d'outils est CallbackReference, et la méthode correspondante est CallbackReference.getCallback, comme indiqué ci-dessous :

    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);
    Copier après la connexion

    Le résultat de sortie est le suivant :

    INFO com.flydean. .CallbackUsage - cbV1 : interface proxy vers la fonction native @0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$VoidCallback)
    INFO com.flydean.CallbackUsage - cbB1 : interface proxy vers la fonction native @0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$ByteC retour en arrière )

    On voit que ces deux Callbacks sont en fait des proxys pour les méthodes natives. Si vous regardez la logique d'implémentation de getCallback en détail :

    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;
            }
        }
    Copier après la connexion

    Vous pouvez voir que sa logique d'implémentation consiste d'abord à déterminer si le type est une interface. S'il ne s'agit pas d'une interface, une erreur sera signalée. Déterminez ensuite s’il s’agit d’un mappage direct. En fait, l'implémentation actuelle de JNA est entièrement le mappage d'interface, donc la logique suivante est d'obtenir le rappel correspondant au pointeur de fonction à partir de pointerCallbackMap. Recherchez ensuite le rappel spécifique en fonction du type transmis.

    S'il n'est pas trouvé, créez un nouveau rappel et enfin stockez le rappel nouvellement créé dans pointerCallbackMap.

    Tout le monde doit noter qu'il existe ici un paramètre clé appelé Pointeur. Lorsque vous l'utilisez réellement, vous devez transmettre un pointeur vers la véritable fonction naitve. Dans l'exemple ci-dessus, par souci de simplicité, nous avons personnalisé un pointeur, ce qui n'a pas beaucoup de signification pratique.

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

    TestLibrary lib = Native.load("testlib", TestLibrary.class);
    Copier après la connexion

    然后分别创建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]);
    Copier après la connexion

    这里我们在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));
    Copier après la connexion

    我们直接在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);
    Copier après la connexion

    然后创建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);
                    }
                }
            };
    Copier après la connexion

    然后调用:

    Native.setCallbackThreadInitializer(cb, init);
    Copier après la connexion

    将callback和CallbackThreadInitializer进行关联。

    最后调用callback方法即可:

    lib.callVoidCallbackThreaded(cb, 2, 2000, "callVoidCallbackThreaded", 0);
    Copier après la connexion

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

    Outils d'IA chauds

    Undresser.AI Undress

    Undresser.AI Undress

    Application basée sur l'IA pour créer des photos de nu réalistes

    AI Clothes Remover

    AI Clothes Remover

    Outil d'IA en ligne pour supprimer les vêtements des photos.

    Undress AI Tool

    Undress AI Tool

    Images de déshabillage gratuites

    Clothoff.io

    Clothoff.io

    Dissolvant de vêtements AI

    AI Hentai Generator

    AI Hentai Generator

    Générez AI Hentai gratuitement.

    Article chaud

    R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
    3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Meilleurs paramètres graphiques
    3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Comment réparer l'audio si vous n'entendez personne
    3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    Où trouver la courte de la grue à atomide atomique
    1 Il y a quelques semaines By DDD

    Outils chauds

    Bloc-notes++7.3.1

    Bloc-notes++7.3.1

    Éditeur de code facile à utiliser et gratuit

    SublimeText3 version chinoise

    SublimeText3 version chinoise

    Version chinoise, très simple à utiliser

    Envoyer Studio 13.0.1

    Envoyer Studio 13.0.1

    Puissant environnement de développement intégré PHP

    Dreamweaver CS6

    Dreamweaver CS6

    Outils de développement Web visuel

    SublimeText3 version Mac

    SublimeText3 version Mac

    Logiciel d'édition de code au niveau de Dieu (SublimeText3)

    Racine carrée en Java Racine carrée en Java Aug 30, 2024 pm 04:26 PM

    Guide de la racine carrée en Java. Nous discutons ici du fonctionnement de Square Root en Java avec un exemple et son implémentation de code respectivement.

    Nombre parfait en Java Nombre parfait en Java Aug 30, 2024 pm 04:28 PM

    Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

    Numéro Armstrong en Java Numéro Armstrong en Java Aug 30, 2024 pm 04:26 PM

    Guide du numéro Armstrong en Java. Nous discutons ici d'une introduction au numéro d'Armstrong en Java ainsi que d'une partie du code.

    Générateur de nombres aléatoires en Java Générateur de nombres aléatoires en Java Aug 30, 2024 pm 04:27 PM

    Guide du générateur de nombres aléatoires en Java. Nous discutons ici des fonctions en Java avec des exemples et de deux générateurs différents avec d'autres exemples.

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

    Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

    Numéro de Smith en Java Numéro de Smith en Java Aug 30, 2024 pm 04:28 PM

    Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

    Questions d'entretien chez Java Spring Questions d'entretien chez Java Spring Aug 30, 2024 pm 04:29 PM

    Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

    Break or Return of Java 8 Stream Forach? Break or Return of Java 8 Stream Forach? Feb 07, 2025 pm 12:09 PM

    Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

    See all articles