【问题标题】:Implementing Android Event Handlers Using C++使用 C++ 实现 Android 事件处理程序
【发布时间】:2020-02-07 21:15:32
【问题描述】:

我有一个 Java 布局设计,我目前正在通过 JNI 将其移植到 C++。我实际上已经完成了这一点,但我目前对如何设置像 setOnClickListener 这样的事件处理程序感到困惑。我已经通过the JNI specification 并没有得到太多的运气。

如果有人可以将以下 sn-p 移植到 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));
}

【问题讨论】:

    标签: android c++ java-native-interface overriding jnienv


    【解决方案1】:

    您展示的语法归结为在编译时创建匿名内部类并插入调用以创建这些类的对象(在范围内具有正确的变量)来代替 new View.OnClickListener() { ... } 表达式。

    我看到以下两个选项:

    • 对于每个不同的接口,您创建一个实现该接口的小型 Java 类,并使用接口方法的 native 实现。这是最直接的方法,但它确实需要您将数十或数百个接口实现保持直截了当。

    • 您使用java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class&lt;?&gt;[] interfaces, InvocationHandler h) 动态创建实现必要接口的对象。这会将每个方法调用重新路由到您的 InvocationHandler 实现,它应该是具有 native Object invoke(Object proxy, Method method, Object[] args) 实现的 Java 类。

      要使所有这些可重用,您可以实现此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 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
    }
    

    【讨论】:

    • 我收到错误的参数错误,使用Proxy.newProxyInstance 对象作为 setOnClickListener 的参数。您能否进一步解释如何处理创建的这个新代理?
    • 请设置编辑。
    • 请提出一个新问题,而不是死掉一个两年前的问题。
    • 很高兴你想要这样。开始看起来很乱,哈哈。提出了新的问题。 stackoverflow.com/questions/70287637/…
    猜你喜欢
    • 2019-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-20
    • 1970-01-01
    • 2019-09-16
    • 2012-09-21
    相关资源
    最近更新 更多