JNI调用Java方法,该方法采用自定义Java接口作为参数
jni call java method which take a custom java interface as parameter
我正在cocos2d-x平台上做一个插件项目,我想写一些c++包装接口从jar
SDK通过JNI调用java方法。我知道如何使用 JNI 调用静态 java 方法,但我对 java 函数中的接口参数感到困惑。我有一个 cpp 函数指针来处理回调:
typedef void (* MyCallback)(int responseCode, string arg1, set<string> arg2);
我想编写一个 cpp 包装器方法,例如:
static void MyCpp::setTags(set<string> tags, MyCallback callback) //it use `JNI` to invoke java method setTags(Context context, Set<String> tags, TagCallback callback).
我想在包装器中调用的 java 方法是
public static void setTags(Context context, Set<String> 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<String> arg2) {
// TODO Auto-generated method stub
}
})
我希望我的 SDK 用户像这样使用我的 cpp 包装器方法:
void setTagCallback(int responseCode, string arg1, set<string> arg2){
//users handle callback themselves.
}
void someOtherMethodInvokeTheCppWrapperMethod(){
MyCallback callback = setTagCallback;
set<string> tags;
MyCpp::setTags(tags,callback);
}
您需要构建一个可以将本机C++函数指针包装在与 TagCallback 兼容的基类中的类:
public class NativeTagCallback : TagCallback
{
protected long cppCallbackPtr;
public NativeTagCallback( long callbackPtr )
{
cppCallbackPtr = callbackPtr;
}
public native void NativeCallback( long callbackPtr, int arg0, String arg1, Set<String> arg2 );
public void callback(int arg0, String arg1, Set<String> 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<string> 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, "<init>", "(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 到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<String> arg2);
}
在 NativeTagCallback.callback()
的C++实现中,您可以获取并转换参数从 Java String
和 Set<String>
对象到本机C++对象,然后使用 JNI GetFieldID()
和 GetLongField()
函数将_callbackPointer
从传递给 JNI C++ 函数的jobject objectOrClass
参数中提取出来。
获得_callbackPointer
后,可以将其强制转换为C++函数指针并调用它。
要使用适配器类,在MyCpp::setTags
中,你可以使用 JNI FindClass()
、GetMethodID()
和 NewObject()
来创建 NativeTagCallback
的实例,将(long)(void *)callback
作为cb
传递给构造函数。(这假定编译器生成的函数指针可以容纳 64 位。对于自由函数指针(相对于方法指针(来说,这应该非常普遍,但值得使用快速测试程序进行检查。然后,将该实例作为其"回调"参数传递给 Java setTags()
方法。
保持NativeTagCallback
私密非常重要,因为它可以用来调用任意地址!如果你想更偏执但更难调试,你可以保留一个C++ id 到函数的映射,只将 id 存储在 NativeTagCallback
中。(这会将可调用地址限制为当前用于实际回调的地址。Id 可以由 native void callback()
取消注册,但映射必须是线程安全的。
它来了(请耐心等待,因为我希望我有意义(我从 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) i.e, 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
}
}
}
调用方将发送类似>>>这样的东西,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 返回时,我们调用了我们实现这些对象的方法。
这也可以为你工作,但你仍然需要将它们保存在映射中,以便你可以确定要调用哪个对象的方法。
- Android:使用 c++ 中的 byte[] 参数调用 java 方法
- 如何在JNI中从线程内部调用JAVA方法
- Qt 调用具有 1 个以上参数的 java 方法
- Android 无法从本机代码调用 Java 方法 JNI
- 是否可以在SO库中公开C API以访问Android JAVA方法
- 如何从 NDK(JNI) 调用特定的 Java 方法
- 从 JNI 调用 getWritableDatabase (SQLCipher java 方法)
- 使用C 修改Java方法/字节码
- 使用 JNI 从 cpp 调用 java 方法时出现异常
- 在Qt中从C ++调用Java方法
- 使用C 获取Java对象来调用Java方法
- JNI:从C++调用JAVA方法,返回对象,引用和GC
- 从 Java 调用 c++,然后从相同的 c++ 方法调用 java 方法
- 使用jni.h在C++中编译java方法时出现构建错误
- 如何从C++/Qt调用QtActivity中的非静态Java方法
- 从Android NDK SIGSEGV调用Java方法
- 从c++调用非静态java方法时出错
- 使本机代码访问java方法和数据成员
- Android NDK调用java方法表单cpp
- 当我从C++调用Java方法时,我应该调用PushLocalFrame和PopLocalFrame吗