JNI 回调到 Java 使用接口

Win*_*ter 5 java-native-interface android-ndk

首先,让我列出我能得到的最好结果。 jni 调用以自定义 java 接口作为参数的 java 方法

这不能回答我的。让我解释一下我的问题。我想按如下方式调用 NDK。

(1)Java -> (2)CPP -> (3)C (新线程) -> (4)CPP -> (5)Java

代码如下。

(1) 爪哇

public interface Callback<T> {
    void success(T result);
}
private native void jniUploadAsync(String imagePath, Callback<String> callback);

jniUploadAsync(file.getAbsolutePath(), new Callback<String>() {
                                        @Override
                                        public void success(final String result) {
                                            Log.v("MyClass: result:: ", result);
                                        }
                                    });
Run Code Online (Sandbox Code Playgroud)

(2) CPP

static JavaVM *jvm;
void imageUploadCallback(char *json, void *completionCallback) {
    JNIEnv *env;
    jint rs = jvm->AttachCurrentThread(&env, NULL);//create JNIEnv from JavaVM    
    jclass cbClass = env->FindClass("org/winster/test/Callback");
    jmethodID method = env->GetMethodID(cbClass, "success", "(Ljava/lang/String;)V");
    env->CallVoidMethod(static_cast<jobject>(completionCallback), method, "abcd");
}

void Java_org_winster_test_MyClass_jniUploadAsync(JNIEnv * env, jobject obj, jstring imagePath, jobject completionCallback) {
    jint rs = env->GetJavaVM(&jvm); //Cache JavaVM here
    CallMyCMethod((char *)filePath, &imageUploadCallback, &completionCallback);
}
Run Code Online (Sandbox Code Playgroud)

(3) 丙

CallMyCMethod() //please assume that it works. The reason I need void* as the type for completionCallback is because, in ObjC implementation I use this
Run Code Online (Sandbox Code Playgroud)

(4) CPP

//Call comes back to imageUploadCallback()
Run Code Online (Sandbox Code Playgroud)

(5) 爪哇

//I expect this Log.v("MyClass: result:: ", result); to be executed
Run Code Online (Sandbox Code Playgroud)

请注意,这不是一个关于如何从 C++ 调用 Java 的基本问题。我要解决的两个具体问题是,如何调用“回调”以及如何调用 Java 接口实现中的方法。我已经为 Obj-C 做了这个,它很简单。

V-m*_*ter 6

(2) 首先,您需要存储对的引用,JavaVM以便JNIEnv以后可以从其他线程获取。还需要从参数中得到的局部变量中获取新的全局引用(不要忘记删除它,否则会导致内存泄漏)。

static JavaVM* jvm = 0;

void Java_org_winster_test_MyClass_jniUploadAsync(JNIEnv * env, jobject obj, jstring imagePath, jobject completionCallback) {
    env->GetJavaVM(&jvm); //store jvm reference for later

    jobject globalref = env->NewGlobalRef(completionCallback);

    CallMyCMethod((char *)filePath, &imageUploadCallback, (void *)globalref);
}
Run Code Online (Sandbox Code Playgroud)

(4) 使用泛型时,native 端无法知道它们是什么类型,所以你使用的所有地方都T应该Object在 JNI/C/CPP 部分使用

您正在 C 中启动新线程。如果您愿意从该线程触发回调,则需要将其连接到 Java 虚拟机,然后将其分离。从我认为你所做的,你只使用回调对象一次。在这种情况下,您还需要删除对它的全局引用。

void imageUploadCallback(char *json, void *completionCallback) {
    JNIEnv* env;
    jvm->AttachCurrentThread(&env, NULL);

    jclass cbClass = env->FindClass("org/winster/test/Callback");
    jmethodID method = env->GetMethodID(cbClass, "success", "(Ljava/lang/Object;)V");
    jstring abcd = env->NewStringUTF("abcd");
    env->CallVoidMethod(completionCallback, method, abcd);

    env->DeleteGlobalRef(completionCallback);
    jvm->DetachCurrentThread();
}
Run Code Online (Sandbox Code Playgroud)