使用 C++ 实现 Android 个事件处理程序
Implementing Android Event Handlers Using C++
我在 Java 中有一个布局设计,我目前正在通过 JNI 将其移植到 C++。我实际上已经完成了这一点,但我目前对我应该如何设置事件处理程序感到困惑,例如 setOnClickListener。我经历了the JNI specification,但运气不佳。
如果有人可以将以下代码片段移植到 C++ 或引导我朝着正确的方向前进(由于结果的代码量会更合理),将不胜感激。
public void setOnClickListener(boolean modification, int index, int commandIndex, final TextView textView){
final int key = index;
final int command = commandIndex;
if(modification) {
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
changingMenu(key, command, textView);
Runnable r = new Runnable() {
@Override
public void run() {
resetMenu(key, command, textView);
}
};
Handler h = new Handler();
h.postDelayed(r, 250);
}
});
return;
}
menuTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleMenu();
}
});
}
编辑: 将错误参数传递给 setOnClickListener
Java
Object getProxy (MyInvocationHandler mih) {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
return java.lang.reflect.Proxy.newProxyInstance(classLoader, new Class[] { }, mih);
}
C++
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->FindClass("com/app/core/MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_IH, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_IH, cst_IH, (jlong)cfunc);
jclass klass = env->FindClass("com/app/core/Activity");
jmethodID method = env->GetMethodID(klass, "getProxy", "(Lcom/app/core/MyInvocationHandler;)Ljava/lang/Object;");
return env->CallObjectMethod(context, method, myIH); //Returning wrong object?
}
jobject aa (JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
__android_log_print(ANDROID_LOG_ERROR, "TEST", "SUCCESS");
}
void setListeners() {
jclass klass = env->FindClass("android/view/View");
jmethodID method = env->GetMethodID(klass, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");
klass = env->FindClass("android/view/View$OnClickListener");
env->CallVoidMethod(imageView, method, createProxyInstance(env, klass, &aa));
}
您显示的语法归结为在编译时创建匿名内部 classes 并插入调用以创建这些 classes 的对象(在范围内具有正确的变量)代替new View.OnClickListener() { ... }
表达式。
我看到以下两个选项:
对于每个不同的接口,您创建一个实现该接口的小 Java class,以及该接口方法的 native
实现。这是最直接的方法,但它确实要求您将数十或数百个接口实现保持直接。
您使用 java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
动态创建实现必要接口的对象。这会将每个方法调用重新路由到您的 InvocationHandler
实现,它应该是具有 native Object invoke(Object proxy, Method method, Object[] args)
实现的 Java class。
为了使所有这些都可重用,您可以实现此 InvocationHandler
来包装一个 std::function
对象,因此对例如 menuTitle.setOnClickListener
的最终调用可能如下所示:
env->CallVoidMethod(menuTitle, menuTitle_setOnClickListener,
createProxyInstance(env, cls_View_OnClickListener, [](JNIEnv *env, jobject proxy, jobject method, jobjectArray args) {
...
});
对于后一种解决方案,定义如下Java class:
class MyInvocationHandler implements InvocationHandler {
private long cfunc;
MyInvocationHandler(long cfunc) { this.cfunc = cfunc; }
public native static Object invoke(Object proxy, Method method, Object[] args);
}
您在 C++ 端实现为:
typedef jobject (*CFunc)(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args)
extern "C" jobject Java_MyInvocationHandler_invoke(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
jclass cls_myIH = env->GetObjectClass(obj);
jfieldID fld_myIH_cfunc = env->GetFieldID(cls_myIH, "cfunc", "J");
CFunc cfunc = (CFunc)env->GetLongField(obj, fld_myIH_cfunc);
cfunc(env, proxy, method, args);
return nullptr;
}
最后,我们可以实现createProxyInstance
如下:
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->GetClass("MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_ID, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_ID, cst_IH, (jlong)cfunc);
// now call Proxy.createProxyInstance with this object as InvocationHandler
}
我在 Java 中有一个布局设计,我目前正在通过 JNI 将其移植到 C++。我实际上已经完成了这一点,但我目前对我应该如何设置事件处理程序感到困惑,例如 setOnClickListener。我经历了the JNI specification,但运气不佳。
如果有人可以将以下代码片段移植到 C++ 或引导我朝着正确的方向前进(由于结果的代码量会更合理),将不胜感激。
public void setOnClickListener(boolean modification, int index, int commandIndex, final TextView textView){
final int key = index;
final int command = commandIndex;
if(modification) {
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
changingMenu(key, command, textView);
Runnable r = new Runnable() {
@Override
public void run() {
resetMenu(key, command, textView);
}
};
Handler h = new Handler();
h.postDelayed(r, 250);
}
});
return;
}
menuTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleMenu();
}
});
}
编辑: 将错误参数传递给 setOnClickListener
Java
Object getProxy (MyInvocationHandler mih) {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
return java.lang.reflect.Proxy.newProxyInstance(classLoader, new Class[] { }, mih);
}
C++
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->FindClass("com/app/core/MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_IH, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_IH, cst_IH, (jlong)cfunc);
jclass klass = env->FindClass("com/app/core/Activity");
jmethodID method = env->GetMethodID(klass, "getProxy", "(Lcom/app/core/MyInvocationHandler;)Ljava/lang/Object;");
return env->CallObjectMethod(context, method, myIH); //Returning wrong object?
}
jobject aa (JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
__android_log_print(ANDROID_LOG_ERROR, "TEST", "SUCCESS");
}
void setListeners() {
jclass klass = env->FindClass("android/view/View");
jmethodID method = env->GetMethodID(klass, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");
klass = env->FindClass("android/view/View$OnClickListener");
env->CallVoidMethod(imageView, method, createProxyInstance(env, klass, &aa));
}
您显示的语法归结为在编译时创建匿名内部 classes 并插入调用以创建这些 classes 的对象(在范围内具有正确的变量)代替new View.OnClickListener() { ... }
表达式。
我看到以下两个选项:
对于每个不同的接口,您创建一个实现该接口的小 Java class,以及该接口方法的
native
实现。这是最直接的方法,但它确实要求您将数十或数百个接口实现保持直接。您使用
java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
动态创建实现必要接口的对象。这会将每个方法调用重新路由到您的InvocationHandler
实现,它应该是具有native Object invoke(Object proxy, Method method, Object[] args)
实现的 Java class。为了使所有这些都可重用,您可以实现此
InvocationHandler
来包装一个std::function
对象,因此对例如menuTitle.setOnClickListener
的最终调用可能如下所示:
env->CallVoidMethod(menuTitle, menuTitle_setOnClickListener,
createProxyInstance(env, cls_View_OnClickListener, [](JNIEnv *env, jobject proxy, jobject method, jobjectArray args) {
...
});
对于后一种解决方案,定义如下Java class:
class MyInvocationHandler implements InvocationHandler {
private long cfunc;
MyInvocationHandler(long cfunc) { this.cfunc = cfunc; }
public native static Object invoke(Object proxy, Method method, Object[] args);
}
您在 C++ 端实现为:
typedef jobject (*CFunc)(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args)
extern "C" jobject Java_MyInvocationHandler_invoke(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
jclass cls_myIH = env->GetObjectClass(obj);
jfieldID fld_myIH_cfunc = env->GetFieldID(cls_myIH, "cfunc", "J");
CFunc cfunc = (CFunc)env->GetLongField(obj, fld_myIH_cfunc);
cfunc(env, proxy, method, args);
return nullptr;
}
最后,我们可以实现createProxyInstance
如下:
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->GetClass("MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_ID, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_ID, cst_IH, (jlong)cfunc);
// now call Proxy.createProxyInstance with this object as InvocationHandler
}