jni调用java方法,该方法将自定义java接口作为参数
我正在cocos2d-x平台上开发插件项目,我想编写一些c ++包装器接口,通过jar
SDK中的JNI调用java方法。 我知道如何使用JNI来调用静态java方法,但我对java函数中的interface参数感到困惑。 我有一个处理回调的cpp函数指针:
typedef void (* MyCallback)(int responseCode, string arg1, set arg2);
我想写一个cpp包装器方法,如:
static void MyCpp::setTags(set tags, MyCallback callback) //it use `JNI` to invoke java method setTags(Context context, Set tags, TagCallback callback).
我想在包装器中调用的java方法是
public static void setTags(Context context, Set tags, TagCallback callback)
TagCallback
是API用户实现的接口。 那么,有可能让TagCallback
最终回调到MyCallback
函数吗? 换句话说,我可以使用jni
将cpp函数指针转换为java接口吗? 谢谢你的耐心 。
编辑:如果用户只想使用java,这里是如何使用setTag
:
public static void setTags(context, tags, new TagCallback{ @Override public void callback(int arg0, String arg1, Set arg2) { // TODO Auto-generated method stub } })
我希望我的SDK用户使用我的cpp包装器方法,如下所示:
void setTagCallback(int responseCode, string arg1, set arg2){ //users handle callback themselves. } void someOtherMethodInvokeTheCppWrapperMethod(){ MyCallback callback = setTagCallback; set tags; MyCpp::setTags(tags,callback); }
首先,您需要构建一个可以在TagCallback兼容基类中包装本机C ++函数指针的类:
public class NativeTagCallback : TagCallback { protected long cppCallbackPtr; public NativeTagCallback( long callbackPtr ) { cppCallbackPtr = callbackPtr; } public native void NativeCallback( long callbackPtr, int arg0, String arg1, Set arg2 ); public void callback(int arg0, String arg1, Set arg2) { NativeCallback( cppCallbackPtr, arg0, arg2, arg2 ); } }
本机代码定义如下:
extern "C" jvoid Java_com_wrapper_NativeTagCallback_NativeCallback( JNIEnv* pEnv, jobject jCaller, jlong cppCallbackPtr, jint arg0, jstring arg1, jobject arg2 ) { MyCallback cppCallback = (MyCallback)cppCallbackPtr; const char* pCString = pEnv->GetStringUTFChars( arg1, 0); string arg1Str( pCString ); pEnv->ReleaseStringUTFChars( arg1, pCString ); set< string > arg2Set = ConvertJavaSetToCPPSet( arg2 ); // Perform your java to CPP set conversion here. cppCallbackPtr( (int)arg0, arg1Str, arg2Set ); }
然后,您将创建相关的类并将其从C ++传递给您的函数,如下所示:
void MyCpp::setTags(set tags, MyCallback callback) { extern __thread JNIEnv* gpEnv; // Get the setTags function. jclass jWrapperClass = gpEnv->FindClass( "com/wrapper/cocoswrapper" ); // Insert the correct class name here. jmethodID jWrapperSetTag = gpEnv->GetStaticMethodID( jWrapperClass, "setTags", "(Landroid/content/Context;Ljava/util/Set;Lcom/wrapper/TagCallback)V;" ); // Get the TagCallback related function jclass jNativeTagCallbackClass = gpEnv->FindClass( "com/wrapper/NativeTagCallback" ); jclass jNativeTagCallbackConstructor = gpEnv->GetMethodID( jNativeTagCallbackClass, "", "(J)V" ); jobject jNativeTagCallbackObject = gpEnv->NewObject( jNativeTagCallbackClass, jNativeTagCallbackConstructor, (jlong)callback) // Make function call. gpEnv->CallStaticVoidMethod( jWrapperClass, jWrapperSetTag, jAndroidContext, tags.GetJNIObject(), jNativeTagCallbackObject ); }
我想说你需要一个实现TagCallback的(私有)Java类来存储C ++函数指针并实现Java-to-C ++回调适配:
private class NativeTagCallback implements TagCallback { private long _callbackPointer; private NativeTagCallback(long cb) { _callbackPointer = cb; } @Override public native void callback(int arg0, String arg1, Set arg2); }
在NativeTagCallback.callback()
的C ++实现中,您将Java String
和Set
对象中的参数提取并转换为本机C ++,然后使用JNI GetFieldID()
和GetLongField()
函数将_callbackPointer
从jobject objectOrClass
参数传递给您的JNI C ++函数。
一旦有了_callbackPointer
,就可以将它转换为C ++函数指针并调用它。
要使用适配器类,在MyCpp::setTags
您将使用JNI FindClass()
, GetMethodID()
和NewObject()
来创建NativeTagCallback
的实例,将(long)(void *)callback
作为cb
传递给构造函数。 (这假设您的编译器生成的函数指针可以适合64位。对于自由函数指针而言,这通常是正确的 – 而不是方法指针 – 但值得用快速测试程序检查。)然后你传递了实例以Java setTags()
方法作为其`callback“参数。
将NativeTagCallback
保持NativeTagCallback
私有非常重要,因为它可用于调用任意地址! 如果你想变得更偏执但更难调试,你可以保留一个C ++ id-to-function映射,只在NativeTagCallback
存储id。 (这会将可调用地址限制为当前用于实际回调的地址。可以通过native void callback()
取消注册ID,但映射需要是线程安全的。)
在这里(跟我一起,我希望我有意义)我从Cocos2dx的“CCHttpRequest”库中得到了这个想法,
在JNI类中,保留一个回调映射。
典型的JNI调用(首先是同步调用,这是一种非常简单的方法),
void requestForItems(int itemId, CCObject* target, SEL_CallFuncND selector) { //U can have a small struct/class that contains 2 fields, a CCObject* and a SEL_CallFuncND) ie, the object and its function //Give this callback/target object a uniqueId, Add this pair(uniqueId, callbackObject) to the map) //make sure u retain the "target" so that it stays alive until u call the callback function after the Java call returns JniMethodInfo t; if (JniHelper::getStaticMethodInfo(t, "com/myproj/folder/AbcManager", "requestItems", "(ILjava/lang/String;)Ljava/lang/String;")) { jstring id = (jstring) t.env->CallStaticObjectMethod(t.classID, t.methodID, itemId, uniqueId); const char* _data = t.env->GetStringUTFChars (id, 0); t.env->DeleteLocalRef(t.classID); //_data >>> is the Id returned, but since we are creating "uniqueId" in this function, this won't be of that importance in a synchronous call /// call this selector function if (target && selector) { (target->*selector)(null, null); //first argument is the sender, the next is a void pointer which u can use to send any information target->release(); //as we retained it earlier } } }
调用者会发送类似>>> this,callfuncND_selector(XyzClass :: abcAction)的内容
现在对于异步调用,上面的函数将更改为
. . . t.env->CallStaticVoidMethod(t.classID, t.methodID, itemId, uniqueId); t.env->DeleteLocalRef(t.classID); }
现在,在上面这个JNI调用的回调函数中,
说方法>>>
JNIEXPORT void JNICALL Java_com_myproj_folder_AbcManager_callingAbcRequestCallback(JNIEnv* env, jobject thiz, jstring data) { const char * _data = env->GetStringUTFChars (data, 0); //here we'll do the remaining work //_data >>> is the Id of the callbackObject //Get the Object from the Map for thie Id // call this selector function if (callbackObject->getTarget() && callbackObject->getSelector()) { (callbackObject->getTarget()->*callbackObject->getSelector())(null, null); } //Now you can delete the object and remove the map entry }
我们做的是我们创建了包含我们需要调用的函数的类,实现了一个接口,然后我们将它们传递给JNI,然后从Java返回,我们调用了我们使这些对象实现的方法。
这也适用于您,但您仍需要将它们保存在地图中,以便您可以识别要调用的对象的方法。
- 在将gradle版本升级到2.3.3时,使用app:transformClassesAndResourcesWithProguardForRelease构建失败
- 如何在Android中以编程方式一个接一个地连接到多个Wifi网络?
- 如何在java中找出与语言环境相关的文本方向?
- 在Universal Image Loader UIL中设置ViewPager的Image Wallaper
- 在深度睡眠中连接到蓝牙设备失败
- 如何解决java中的双精度问题
- Android:将XML从原始文件夹转换为字符串
- 在圆形霍夫变换中,什么是累加器分辨率的反比(dp)以及它如何影响圆检测?
- 如何在AndEngine中更改TimerHandler延迟?