Make writing a habit together! This is the 12th day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

Introduction to the

What is callback? To put it simply, a callback is a callback notification, which is used when we need to notify a particular task after a method has completed or an event has been fired.

The language you’re most likely to see callback in is javascript, where it’s almost ubiquitous. To solve the problem of callback causing callback hell, PROMISES were specifically introduced in ES6 to solve this problem.

To facilitate interaction with native methods, JNA also provides Callback for callbacks. The essence of a callback in JNA is a pointer to a native function that calls a method in a native function.

The Callback JNA

Let’s 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"));
}
Copy the code

All Callback methods need to implement this Callback interface. The Callback interface is simple, defining an interface and two attributes.

Start with this interface, the interface is called UncaughtExceptionHandler, there is a uncaughtException method. This interface is primarily used to handle exceptions that are not caught in JAVA’s callback code.

Note that no exceptions can be thrown in the uncaughtException method, and any exceptions thrown from this method are ignored.

METHOD_NAME Specifies the method to be called by the Callback.

If only one public method is defined in the Callback class, the default Callback method is that 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 property is FORBIDDEN_NAMES. Indicates that names in this list cannot be used as callback methods.

Three method names cannot be used: “hashCode”, “equals”, and “toString”.

Callback has a sibling called DLLCallback.

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

DLLCallback is primarily used for access to Windows apis.

In the case of callback objects, we are responsible for releasing the callback objects ourselves. If the native code attempts to access a callback that has been reclaimed, the VM may crash.

The application of the callback

The definition of the callback

Because callback in JNA actually maps Pointers to functions in Native. First look at the function pointer defined in struct:

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

In this structure, two function Pointers are defined, taking two parameters and one parameter respectively.

The corresponding JNA callback is defined 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;
}
Copy the code

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

Then look at how this is called:

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

Callback can also be used as a return value of a function, as shown below:

typedef void (*sig_t)(int);
sig_t signal(int signal, sig_t sigfunc);
Copy the code

For such a separate function pointer, we need to define a Library in which the corresponding Callback is defined, as follows:

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

Get and apply callback

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

If the callback definition were in a normal Library, it would look like this:

    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);
    }
Copy the code

In the example above, we define two callbacks in a Library, one that returns no value and one that returns byte.

JNA provides a simple tools to help us get the Callback, the utility class is CallbackReference and 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);
Copy the code

The following output is displayed:

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)
Copy the code

As you can see, these two callbacks are actually proxies for native methods. 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; }}Copy the code

As you can see, the implementation logic is to first check if type is interface, and if it is not, an error will be reported. Then check whether it is direct mapping. In fact, current JNA implementations are interface Mapping, so the next logical step is to get the callback corresponding to the function pointer from pointerCallbackMap. The specific Callback is then found based on the type passed in.

If none is found, a new callback is created, and the newly created callback is stored in the pointerCallbackMap.

Note that there is a key parameter called Pointer that you need to pass in to the actual naitve function. In the example above, for simplicity’s sake, we define a Pointer that doesn’t have much practical meaning.

If you really want to call the two call methods created in TestLibrary: callVoidCallback and callInt8Callback in JNA, you first need to load the corresponding Library: callVoidCallback

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

VoidCallback = testLibrary. VoidCallback = testLibrary. ByteCallback = testLibrary. 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]);
Copy the code

Here we write the value of voidCalled back to true in callback to indicate that the callback method has been called.

Now look at the ByteCallback with the return value:

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));Copy the code

We simply return the byte value to be returned in the callback method.

Use callback in a multithreaded environment

By default, the callback method is executed in the current thread. If you want to the callback method is executed in another thread, you can create a CallbackThreadInitializer, specify the daemon, detach, name, and the threadGroup attributes:

        final String tname = "VoidCallbackThreaded";
        ThreadGroup testGroup = new ThreadGroup("Thread group for callVoidCallbackThreaded");
        CallbackThreadInitializer init = new CallbackThreadInitializer(true, false, tname, testGroup);
Copy the code

Then create an instance of 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); }}};Copy the code

Then call:

 Native.setCallbackThreadInitializer(cb, init);
Copy the code

The callback and CallbackThreadInitializer association.

Finally, call the callback method:

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

conclusion

Callback in JNA can pass methods to native methods, which can be very useful in some cases.

The code for this article: github.com/ddean2009/l…

This article is available at www.flydean.com/09-jna-call…

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!