如何通过C ++回调作为android中Button的单击处理程序?

问题描述 投票:1回答:1

我正在构建一个Android应用,我需要从JNI部分动态创建UI元素,并将C ++函数分配为单击处理程序。

我已经定义了一个Java函数来创建一个按钮并返回它。我有一个C ++函数,该函数调用Java函数并具有按钮的jobject。现在,我想为该对象分配另一个C ++函数作为单击处理程序。

Java:

public class MainActivity extends AppCompatActivity {
...
    public View control(String text) {
        Button bt = new Button(this);
        bt.setText(text);
        ((ConstraintLayout)findViewById(R.id.layout_main)).addView(bt);
        return bt;
    }
}

C ++:

extern "C" JNIEXPORT void JNICALL Java_com_shaidin_ebellum_MainActivity_start(JNIEnv *env, jobject me)
{
    jmethodID jControl = env->GetMethodID(env->GetObjectClass(me), "control", "(Ljava/lang/String;)Landroid/view/View;");
    jstring jText = env->NewStringUTF("test");
    jobject jView = env->CallObjectMethod(me, jControl, jText);

    // How to add listener here?

    env->DeleteLocalRef(jView_);
    env->DeleteLocalRef(jText);
}

在这个问题上Android UI in C++JNIpp库https://github.com/DmitrySkiba/itoa-jnipp已经介绍了它,并且它确实可以实现我想要的功能,但是我想自己实现它,因为我不想在项目中添加那么多代码。另外,该库具有使用嵌套宏的复杂实现,因此很难了解其工作原理。

java android c++ java-native-interface
1个回答
0
投票

使Java对象执行C ++代码回调的过程几乎总是相同的;使Java对象存储对本机对象的某种引用(通常是一个指针),并将其传递回可以调用适当函数的C ++代码。

一个潜在的实现可能看起来像这样:

MyOnClickListener.java

public class MyOnClickListener implements View.OnClickListener {
    private final long nativePeer;

    public MyOnClickListener(final long nativePeer) {
        this.nativePeer = nativePeer;
    }

    @Override
    public void onClick(View v) {
        OnClick(nativePeer);
    }

    @Override
    public void finalize() {
        Release(nativePeer);
    }

    private static native void OnClick(final long peer);
    private static native void Release(final long peer);
}

C ++

extern "C" JNIEXPORT void Java_com_example_myapp_MyOnClickListener_OnClick(
        JNIEnv *env,
        jclass clazz,
        jlong nativePeer) {
    auto f = reinterpret_cast<std::function<void(void)>*>(nativePeer);
    (*f)();
}

extern "C" JNIEXPORT void Java_com_example_myapp_MyOnClickListener_Release(
        JNIEnv *env,
        jclass clazz,
        jlong nativePeer) {
    auto f = reinterpret_cast<std::function<void(void)>*>(nativePeer);
    delete f;
}

...

// Setting the OnClickListener

// We allocate this object with `new` since we need it to remain alive
// until the Java code no longer needs it. It is the responsibility of
// the Java code to ensure that the memory gets freed.
auto callback = new std::function<void(void)>([] {
    __android_log_print(ANDROID_LOG_WARN, "MyOnClickListener", "Hello from native onClick!");
});

jclass listener_clazz = env->FindClass("com/example/myapp/MyOnClickListener");
jmethodID new_listener = env->GetMethodID(listener_clazz, "<init>", "(J)V");
jobject listener = env->NewObject(listener_clazz, new_listener, callback);

jmethodID set_onclicklistener = env->GetMethodID(button_clazz, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");

env->CallVoidMethod(button, set_onclicklistener, listener);

注1:当然,在实际代码中,您应该添加适当的错误处理,而不应盲目地认为每个JNI调用均成功。

注2:当您不再需要本机对等对象时,可能不想显式调用finalize函数,而不必依赖于它的调用。

注3:在我的示例中,我使用了指向Release的指针。您可能要使用指向您自己的某个类的指针。这实际上取决于您自己决定。

© www.soinside.com 2019 - 2024. All rights reserved.