私の C プログラムは、定期的に呼び出されるコールバック関数を使用しています。Java または C# プログラムでコールバック関数を処理できるようにしたいと考えています。これを実現するには、.i ファイルをどのように記述すればよいですか?
C コールバックは次のようになります。
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)
コールバックでデータを渡す機会があれば、これを行うことができますが、JNI グルーを記述する必要があります。C スタイルのコールバックを Java インターフェイスにマップする方法の完全な例をまとめました。
最初に行う必要があるのは、Java 側で適切なインターフェースを決定することです。C では、次のようなコールバックがあると仮定しました。
typedef void (*callback_t)(int arg, void *userdata);
私はそれを Java で次のように表現することにしました。
public interface Callback {
public void handle(int value);
}
void *userdata
( Java 側での の損失は実際の問題ではありません。なぜなら、簡単Object
に実装するに状態を格納できるからCallback
です)。
次に、ラッピングを実行するために、次のヘッダー ファイルを作成しました (実際には単なるヘッダーであってはなりませんが、物事をシンプルに保ちます)。
typedef void (*callback_t)(int arg, void *data);
static void *data = NULL;
static callback_t active = NULL;
static void set(callback_t cb, void *userdata) {
active = cb;
data = userdata;
}
static void dispatch(int val) {
active(val, data);
}
この C を次のインターフェイスで正常にラップすることができました。
%module test
%{
#include <assert.h>
#include "test.h"
// 1:
struct callback_data {
JNIEnv *env;
jobject obj;
};
// 2:
void java_callback(int arg, void *ptr) {
struct callback_data *data = ptr;
const jclass callbackInterfaceClass = (*data->env)->FindClass(data->env, "Callback");
assert(callbackInterfaceClass);
const jmethodID meth = (*data->env)->GetMethodID(data->env, callbackInterfaceClass, "handle", "(I)V");
assert(meth);
(*data->env)->CallVoidMethod(data->env, data->obj, meth, (jint)arg);
}
%}
// 3:
%typemap(jstype) callback_t cb "Callback";
%typemap(jtype) callback_t cb "Callback";
%typemap(jni) callback_t cb "jobject";
%typemap(javain) callback_t cb "$javainput";
// 4:
%typemap(in,numinputs=1) (callback_t cb, void *userdata) {
struct callback_data *data = malloc(sizeof *data);
data->env = jenv;
data->obj = JCALL1(NewGlobalRef, jenv, $input);
JCALL1(DeleteLocalRef, jenv, $input);
$1 = java_callback;
$2 = data;
}
%include "test.h"
インターフェイスにはかなりの部分があります。
struct
Java インターフェイスを呼び出すために必要な情報を格納するための。callback_t
。定義したばかりのユーザー データを受け取り、struct
標準の JNI を使用して Java インターフェイスへの呼び出しをディスパッチします。Callback
オブジェクトを実数として C 実装に直接渡すタイプマップjobject
。void*
Java 側で を非表示にし、callback
データを設定し、実際の関数の対応する引数を埋めて、呼び出しを Java にディスパッチするために記述したばかりの関数を使用するtypemap 。その後のガベージ コレクションを防ぐために、Java オブジェクトへのグローバル参照が必要です。私はそれをテストするために小さなJavaクラスを書きました:
public class run implements Callback {
public void handle(int val) {
System.out.println("Java callback - " + val);
}
public static void main(String argv[]) {
run r = new run();
System.loadLibrary("test");
test.set(r);
test.dispatch(666);
}
}
あなたが望むように働いた。
注意すべき点:
set
、グローバル参照がリークします。コールバックを設定解除する方法を提供するか、複数回設定しないようにするか、代わりに弱参照を使用する必要があります。JNIEnv
複数のスレッドがある場合は、私がここにいるよりも賢くする必要があります。%constant
、これらのタイプマップにより、ラップされた関数がそのような入力を受け入れることができなくなります。おそらく、それを回避するためにオーバーロードを提供したいと思うでしょう。この質問には、さらに良いアドバイスがあります。
C# のソリューションは、タイプマップ名が異なり、C で記述したコールバック関数の実装が異なるため、多少似ていると思います。
C# ソリューション: デリゲートを使用する必要があります。コードサンプルを見てください
public delegate void DoSome(object sender);
public class MyClass{
public event DoSome callbackfunc;
public void DoWork(){
// do work here
if(callbackfunc != null)
callbackfunc(something);
}
}
これはイベント処理メカニズムにも似ていますが、理論的にはどちらも c# で同じ実装を持っています
Java ソリューション: インターフェイスを使用する必要があります。このサンプル コードを見てください。
interface Notification{
public void somthingHappend(Object obj);
}
class MyClass{
private Notification iNotifer;
public void setNotificationReciver(Notification in){
this.iNotifier = in;
}
public void doWork(){
//some work to do
if(something heppens){ iNotifier.somthingHappend(something);}
}
}
実際には、List of Notification を使用して一連のコールバック レシーバーを実装できます。