目次
はじめに
JNA でのコールバック
コールバックの適用
コールバックの定義
コールバックの取得と適用
在多线程环境中使用callback
ホームページ Java &#&チュートリアル 高度な Java 使用法で JNA のコールバック問題を解決する方法

高度な Java 使用法で JNA のコールバック問題を解決する方法

May 05, 2023 am 11:37 AM
java jna

    はじめに

    コールバックとは何ですか?簡単に言うと、コールバックはコールバック通知であり、メソッドが完了したりイベントがトリガーされた後に特定のタスクに通知する必要がある場合は、コールバックを使用する必要があります。

    コールバックを最もよく目にする言語は JavaScript ですが、基本的に JavaScript ではコールバックがあらゆる場所に存在します。コールバックによるコールバック地獄の問題を解決するために、ES6 ではこの問題を解決するために特別に Promise が導入されました。

    ネイティブ メソッドとの対話を容易にするために、JNA はコールバック用の Callback も提供します。 JNA のコールバックの本質は、ネイティブ関数へのポインタです。このポインタを通じて、ネイティブ関数のメソッドを呼び出すことができます。見てみましょう。

    JNA でのコールバック

    最初に、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"));
    }
    ログイン後にコピー

    すべてのコールバック メソッドは、この Callback インターフェイスを実装する必要があります。 Callback インターフェイスは非常にシンプルで、1 つのインターフェイスと 2 つのプロパティを定義します。

    まずこのインターフェイスを見てみましょう。インターフェイス名は UncaughtExceptionHandler で、その中には uncaughtException メソッドがあります。このインターフェイスは主に、JAVA のコールバック コードでキャッチされない例外を処理するために使用されます。

    uncaughtException メソッドでは例外をスローできず、このメソッドからスローされた例外は無視されることに注意してください。

    METHOD_NAME このフィールドは、コールバックによって呼び出されるメソッドを指定します。

    Callback クラスにパブリック メソッドが 1 つだけ定義されている場合、デフォルトのコールバック メソッドはこのメソッドになります。 Callback クラスに複数のパブリック メソッドが定義されている場合、METHOD_NAME = "callback" のメソッドがコールバックとして選択されます。

    最後の属性は FORBIDDEN_NAMES です。このリスト内の名前はコールバック メソッドとして使用できないことを示します。

    現在使用できないメソッド名は「hashCode」「equals」「toString」の3つのようです。

    Callback には DLLCallback という兄弟もあります。DLLCallback の定義を見てみましょう:

    public interface DLLCallback extends Callback {
        @java.lang.annotation.Native
        int DLL_FPTRS = 16;
    }
    ログイン後にコピー

    DLLCallback は主に Windows API にアクセスするために使用されます。

    コールバック オブジェクトの場合、コールバック オブジェクトを自分で解放する必要があります。ネイティブ コードがリサイクルされたコールバックにアクセスしようとすると、VM がクラッシュする可能性があります。

    コールバックの適用

    コールバックの定義

    JNA のコールバックは実際にはネイティブの関数にポインタをマップするためです。まず、構造体で定義されている関数ポインターを見てください。

    struct _functions {
      int (*open)(const char*,int);
      int (*close)(int);
    };
    ログイン後にコピー

    この構造体では、2 つの関数ポインターが定義されており、それぞれ 2 つのパラメーターと 1 つのパラメーターがあります。

    対応する JNA コールバック定義は次のとおりです。

    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;
    }
    ログイン後にコピー

    Callback を継承する 2 つのインターフェイスを Structure に定義し、対応する呼び出しメソッドは対応するインターフェイスで定義されます。

    次に、具体的な呼び出しメソッドを見てみましょう:

    Functions funcs = new Functions();
    lib.init(funcs);
    int fd = funcs.open.invoke("myfile", 0);
    funcs.close.invoke(fd);
    ログイン後にコピー

    さらに、以下に示すように、Callback を関数の戻り値として使用することもできます:

    typedef void (*sig_t)(int);
    sig_t signal(int signal, sig_t sigfunc);
    ログイン後にコピー

    この種の別個の関数ポインターの場合、以下に示すように、ライブラリをカスタマイズし、その中で対応するコールバックを定義する必要があります。

    public interface CLibrary extends Library {
        public interface SignalFunction extends Callback {
            void invoke(int signal);
        }
        SignalFunction signal(int signal, SignalFunction func);
    }
    ログイン後にコピー

    コールバックの取得と適用

    コールバックが定義されている場合Structure の場合、Structure は初期化時に自動的にインスタンス化されるため、Structure から対応するプロパティにアクセスするだけで済みます。

    コールバックが通常のライブラリで定義されている場合、次のようになります。

    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);
        }
    ログイン後にコピー

    上の例では、ライブラリに 2 つのコールバックを定義しました。1 つは戻り値のないコールバック、もう 1 つは戻り値のないコールバックです。バイトを返すコールバック。

    JNA は、Callback の取得に役立つ単純なツール クラスを提供します。このツール クラスは CallbackReference で、対応するメソッドは CallbackReference.getCallback です。次に示すように:

    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);
    ログイン後にコピー

    出力結果は次のとおりです。 :

    INFO com.flydean.CallbackUsage - cbV1:ネイティブ関数へのプロキシ インターフェイス@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$VoidCallback)
    INFO com.flydean.CallbackUsage - cbB1:Proxyネイティブ関数へのインターフェイス@0xffffffffc46eeefc (com.flydean.CallbackUsage$TestLibrary$ByteCallback)

    これら 2 つのコールバックが実際にはネイティブ メソッドのプロキシであることがわかります。 getCallback の実装ロジックを詳しく見ると、

    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;
            }
        }
    ログイン後にコピー

    その実装ロジックでは、まず型がインターフェイスであるかどうかを判断し、インターフェイスでない場合はエラーが報告されることがわかります。次に、それが直接マッピングであるかどうかを判断します。実際、JNA の現在の実装はすべてインターフェイス マッピングであるため、次のロジックは関数ポインターに対応するコールバックを pointerCallbackMap から取得することです。次に、渡されたタイプに従って特定のコールバックを見つけます。

    見つからない場合は、新しいコールバックを作成し、最後に新しく作成したコールバックを pointerCallbackMap に保存します。

    ここには Pointer という重要なパラメータがあることに注意してください。実際に使用するときは、実際の単純な関数へのポインタを渡す必要があります。上の例では、簡単にするためにポインターをカスタマイズしましたが、実際にはあまり意味がありません。

    如果真的要想在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);
    ログイン後にコピー

    以上が高度な Java 使用法で JNA のコールバック問題を解決する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

    AIヘンタイを無料で生成します。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    Javaの平方根 Javaの平方根 Aug 30, 2024 pm 04:26 PM

    Java の平方根のガイド。ここでは、Java で平方根がどのように機能するかを、例とそのコード実装をそれぞれ示して説明します。

    Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

    Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

    Java の乱数ジェネレーター Java の乱数ジェネレーター Aug 30, 2024 pm 04:27 PM

    Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

    ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

    Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

    Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

    Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

    Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

    この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

    Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

    Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

    Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

    Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

    See all articles