Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JNI Callback to Java using Interface

First of all, let me list the best result I could fetch. jni call java method which take a custom java interface as parameter

This does not answer mine. Let me explain my problem. I want to make a call to NDK as follows.

(1)Java -> (2)CPP -> (3)C (new thread) -> (4)CPP -> (5)Java

Code is below.

(1) Java

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);
                                        }
                                    });

(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);
}

(3) C

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

(4) CPP

//Call comes back to imageUploadCallback()

(5) Java

//I expect this Log.v("MyClass: result:: ", result); to be executed

Please note that, this is not a basic question about how to call Java from C++. The 2 specific points I want to resolve is, how to call the "callback" and how to invoke a method in a Java Interface implementation. I have done this for Obj-C where it is straight forward.

like image 872
Winster Avatar asked Mar 27 '17 11:03

Winster


People also ask

What is JNI (Java Native Interface)?

Introduction At times, it is necessary to use native (non-Java) codes (e.g., C/C++) to overcome the memory management and performance constraints in Java. Java supports native codes via the Java Native Interface (JNI). JNI is difficult, as it involves two languages and runtimes. I shall assume that you are familiar with: Java.

How to implement callback using interfaces in Java?

Callback using Interfaces in Java 1 Create an interface ClickEventHandler with a single method handleClick (). 2 Create a ClickHandler class which implements this interface ClickEventHandler. 3 Create a Button class which will call ClickHandler when it's click method is called. 4 Test the application. More ...

How to callback a static method in JNI?

To callback a static method, use GetStaticMethodID (), CallStatic<Primitive-type>Method (), CallStaticVoidMethod () or CallStaticObjectMethod (). The JNI functions for calling back instance method and static method are:

How do I call a JNI function in C?

The naming convention for the C function is Java_ {package_and_classname}_ {function_name} (JNI_arguments). The dot in package name is replaced by underscore. The arguments are: JNIEnv*: reference to JNI environment, which lets you access all the JNI functions.


1 Answers

(2) First of, you need to store reference to JavaVM so you will be able to obtain JNIEnv later from other thread. Also you need to get new global reference from local variable got from parameter (don't forgot to delete it when it is no longer needed, or it will cause memory leak).

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);
}

(4) When using generics, native side can't know what type they are of, so everywhere you are using T you should be using Object on the JNI/C/CPP part

you are starting new thread in C. If you are willing to fire callback from that thread, you need to connect it to the java virtual machine and detach it afterwards. From what i think you do, you use the callback object only once. In that case you also need to delete global ref to it.

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();
}
like image 73
V-master Avatar answered Sep 30 '22 23:09

V-master