Реализация обработчиков событий Android с использованием C ++ - PullRequest
0 голосов
/ 10 октября 2019

У меня есть дизайн макета на Java, который я сейчас переношу на C ++ через JNI. Я практически закончил на этом этапе, но в настоящее время я озадачен тем, как я должен настроить обработчики событий, такие как setOnClickListener, например. Я прошел через спецификацию JNI и мне не очень повезло.

Если кто-то может перенести следующий фрагмент на 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();
            }
        });
    }

EDIT 1 Я не совсем уверен, как идти о создании функции createProxyInstance. У меня есть следующие аргументы для вызова createProxyInstance:

jclass proxyClass = globalEnv->FindClass("java/lang/reflect/Proxy");
jclass viewClass = globalEnv->FindClass("android/view/View");
jmethodID menuTitle_setOnClickListenerID = globalEnv->GetMethodID(viewClass, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");
jclass view_OnClickListenerClass = globalEnv->FindClass("android/view/View$OnClickListener");

У меня есть доступ к этому newProxyInstance methodID как таковой:

jmethodID newProxyInstance = globalEnv->GetStaticMethodID(proxyClass,"newProxyInstance","(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;");

1 Ответ

0 голосов
/ 10 октября 2019

Синтаксис, который вы показываете, сводится к созданию анонимных внутренних классов во время компиляции и вставке вызовов для создания объектов этих классов (с правильными переменными в области видимости) вместо выражения new View.OnClickListener() { ... }.

Iсм. следующие две опции:

  • Для каждого отдельного интерфейса вы создаете небольшой класс Java, который реализует интерфейс, с реализацией native метода (ов) интерфейса. Это наиболее прямой подход, но он требует, чтобы вы сохранили десятки или сотни реализаций интерфейса.

  • Вы используете java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) для динамического создания объектов, которые реализуют необходимые интерфейсы. Это перенаправит каждый вызов метода в вашу реализацию InvocationHandler, которая должна быть классом Java с реализацией native Object invoke(Object proxy, Method method, Object[] args).

    Чтобы сделать все это многоразовым, вы можете реализовать этот 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
}
...