search
HomeJavajavaTutorialHow to solve the callback problem in JNA in advanced java usage

    Introduction

    What is a callback? To put it simply, callback is a callback notification. When we need to notify certain tasks after a method is completed or an event is triggered, we need to use callback.

    The language you are most likely to see callback in is javascript. Basically in javascript, callback is everywhere. In order to solve the problem of callback hell caused by callbacks, promises were specially introduced in ES6 to solve this problem.

    In order to facilitate interaction with native methods, JNA also provides Callback for callback. The essence of a callback in JNA is a pointer to a native function. Through this pointer, methods in the native function can be called. Let's take a look.

    Callback in JNA

    First look at the definition of Callback in 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"));
    }

    All Callback methods need to implement this Callback interface. The Callback interface is very simple. It defines an interface and two properties.

    Let’s look at this interface first. The interface name is UncaughtExceptionHandler, and there is an uncaughtException method in it. This interface is mainly used to handle exceptions that are not caught in JAVA's callback code.

    Note that in the uncaughtException method, exceptions cannot be thrown, and any exceptions thrown from this method will be ignored.

    METHOD_NAME This field specifies the method to be called by Callback.

    If only one public method is defined in the Callback class, then the default callback method is this method. If multiple public methods are defined in the Callback class, the method with METHOD_NAME = "callback" will be selected as the callback.

    The last attribute is FORBIDDEN_NAMES. Indicates that the names in this list cannot be used as callback methods.

    Currently it seems that there are three method names that cannot be used, namely: "hashCode", "equals", and "toString".

    Callback also has a sibling called DLLCallback. Let’s take a look at the definition of DLLCallback:

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

    DLLCallback is mainly used for accessing Windows API.

    For callback objects, we need to be responsible for releasing the callback objects ourselves. If native code attempts to access a recycled callback, it may cause the VM to crash.

    Application of callback

    Definition of callback

    Because callback in JNA actually maps a pointer to a function in native. First, take a look at the function pointers defined in the struct:

    struct _functions {
      int (*open)(const char*,int);
      int (*close)(int);
    };

    In this structure, two function pointers are defined, with two parameters and one parameter respectively.

    The corresponding JNA callback definition is as follows:

    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;
    }

    We define two interfaces in Structure that inherit from Callback, and the corresponding invoke method is defined in the corresponding interface.

    Then take a look at the specific calling method:

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

    In addition, Callback can also be used as the return value of the function, as shown below:

    typedef void (*sig_t)(int);
    sig_t signal(int signal, sig_t sigfunc);

    For this kind of separate function pointer , we need to customize a Library and define the corresponding Callback in it, as shown below:

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

    Acquisition and application of callback

    If the callback is defined in Structure, then it can be Structure is automatically instantiated when it is initialized, and then you only need to access the corresponding properties from Structure.

    If the callback is defined in an ordinary Library, it is as follows:

    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);
        }

    In the above example, we defined two callbacks in a Library, one with no return value callback, one is a callback that returns byte.

    JNA provides a simple tool class to help us get Callback. This tool class is CallbackReference, and the corresponding method is CallbackReference.getCallback, as shown below:

    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);

    The output results are as follows:

    INFO com.flydean.CallbackUsage - cbV1:Proxy interface to native function@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$VoidCallback)
    INFO com.flydean.CallbackUsage - cbB1:Proxy interface to native function@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$ByteCallback)

    It can be seen that these two Callbacks are actually proxies for the native method. If you look at the implementation logic of getCallback in detail:

    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;
            }
        }

    You can see that its implementation logic is to first determine whether the type is an interface. If it is not an interface, an error will be reported. Then determine whether it is direct mapping. In fact, the current implementation of JNA is all interface mapping, so the next logic is to obtain the callback corresponding to the function pointer from pointerCallbackMap. Then find the specific Callback according to the type passed in.

    If it is not found, create a new callback, and finally store the newly created callback in pointerCallbackMap.

    Everyone should note that there is a key parameter here called Pointer. When actually using it, you need to pass in a pointer to the real naitve function. In the above example, for the sake of simplicity, we customized a Pointer, which does not have much practical significance.

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

    TestLibrary lib = Native.load("testlib", TestLibrary.class);

    然后分别创建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]);

    这里我们在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));

    我们直接在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);

    然后创建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);
                    }
                }
            };

    然后调用:

    Native.setCallbackThreadInitializer(cb, init);

    将callback和CallbackThreadInitializer进行关联。

    最后调用callback方法即可:

    lib.callVoidCallbackThreaded(cb, 2, 2000, "callVoidCallbackThreaded", 0);

    The above is the detailed content of How to solve the callback problem in JNA in advanced java usage. For more information, please follow other related articles on the PHP Chinese website!

    Statement
    This article is reproduced at:亿速云. If there is any infringement, please contact admin@php.cn delete
    How does the class loader subsystem in the JVM contribute to platform independence?How does the class loader subsystem in the JVM contribute to platform independence?Apr 23, 2025 am 12:14 AM

    The class loader ensures the consistency and compatibility of Java programs on different platforms through unified class file format, dynamic loading, parent delegation model and platform-independent bytecode, and achieves platform independence.

    Does the Java compiler produce platform-specific code? Explain.Does the Java compiler produce platform-specific code? Explain.Apr 23, 2025 am 12:09 AM

    The code generated by the Java compiler is platform-independent, but the code that is ultimately executed is platform-specific. 1. Java source code is compiled into platform-independent bytecode. 2. The JVM converts bytecode into machine code for a specific platform, ensuring cross-platform operation but performance may be different.

    How does the JVM handle multithreading on different operating systems?How does the JVM handle multithreading on different operating systems?Apr 23, 2025 am 12:07 AM

    Multithreading is important in modern programming because it can improve program responsiveness and resource utilization and handle complex concurrent tasks. JVM ensures the consistency and efficiency of multithreads on different operating systems through thread mapping, scheduling mechanism and synchronization lock mechanism.

    What does 'platform independence' mean in the context of Java?What does 'platform independence' mean in the context of Java?Apr 23, 2025 am 12:05 AM

    Java's platform independence means that the code written can run on any platform with JVM installed without modification. 1) Java source code is compiled into bytecode, 2) Bytecode is interpreted and executed by the JVM, 3) The JVM provides memory management and garbage collection functions to ensure that the program runs on different operating systems.

    Can Java applications still encounter platform-specific bugs or issues?Can Java applications still encounter platform-specific bugs or issues?Apr 23, 2025 am 12:03 AM

    Javaapplicationscanindeedencounterplatform-specificissuesdespitetheJVM'sabstraction.Reasonsinclude:1)Nativecodeandlibraries,2)Operatingsystemdifferences,3)JVMimplementationvariations,and4)Hardwaredependencies.Tomitigatethese,developersshould:1)Conduc

    How does cloud computing impact the importance of Java's platform independence?How does cloud computing impact the importance of Java's platform independence?Apr 22, 2025 pm 07:05 PM

    Cloud computing significantly improves Java's platform independence. 1) Java code is compiled into bytecode and executed by the JVM on different operating systems to ensure cross-platform operation. 2) Use Docker and Kubernetes to deploy Java applications to improve portability and scalability.

    What role has Java's platform independence played in its widespread adoption?What role has Java's platform independence played in its widespread adoption?Apr 22, 2025 pm 06:53 PM

    Java'splatformindependenceallowsdeveloperstowritecodeonceandrunitonanydeviceorOSwithaJVM.Thisisachievedthroughcompilingtobytecode,whichtheJVMinterpretsorcompilesatruntime.ThisfeaturehassignificantlyboostedJava'sadoptionduetocross-platformdeployment,s

    How do containerization technologies (like Docker) affect the importance of Java's platform independence?How do containerization technologies (like Docker) affect the importance of Java's platform independence?Apr 22, 2025 pm 06:49 PM

    Containerization technologies such as Docker enhance rather than replace Java's platform independence. 1) Ensure consistency across environments, 2) Manage dependencies, including specific JVM versions, 3) Simplify the deployment process to make Java applications more adaptable and manageable.

    See all articles

    Hot AI Tools

    Undresser.AI Undress

    Undresser.AI Undress

    AI-powered app for creating realistic nude photos

    AI Clothes Remover

    AI Clothes Remover

    Online AI tool for removing clothes from photos.

    Undress AI Tool

    Undress AI Tool

    Undress images for free

    Clothoff.io

    Clothoff.io

    AI clothes remover

    Video Face Swap

    Video Face Swap

    Swap faces in any video effortlessly with our completely free AI face swap tool!

    Hot Tools

    SublimeText3 Linux new version

    SublimeText3 Linux new version

    SublimeText3 Linux latest version

    VSCode Windows 64-bit Download

    VSCode Windows 64-bit Download

    A free and powerful IDE editor launched by Microsoft

    MinGW - Minimalist GNU for Windows

    MinGW - Minimalist GNU for Windows

    This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

    Dreamweaver Mac version

    Dreamweaver Mac version

    Visual web development tools

    DVWA

    DVWA

    Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software