2017-03-27 8 views
1

Zunächst möchte ich das beste Ergebnis auflisten, das ich abrufen konnte. jni call java method which take a custom java interface as parameterJNI Callback zu Java mit der Schnittstelle

Dies beantwortet meine nicht. Lass mich mein Problem erklären. Ich möchte NDK wie folgt anrufen.

(1) Java -> (2) CPP -> (3) C (neuer Thread) -> (4) CPP -> (5) Java

Code ist unten.

(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 

Bitte beachten Sie, dass dies nicht eine grundlegende Frage, wie Java von C++ nennen. Die 2 spezifischen Punkte, die ich auflösen möchte, ist, wie man den "Callback" aufruft und wie man eine Methode in einer Java Interface Implementierung aufruft. Ich habe dies für Obj-C getan, wo es geradlinig ist.

+0

und welchen Fehler haben Sie? –

+1

Es ist unklar, woher 'env' in' imageUploadCallback' kommt. Es ist sehr wahrscheinlich, dass Sie 'AttachCurrentThread' aufrufen müssen. – Michael

+0

JNI ERKENNUNG FEHLER IN ANWENDUNG: Verwendung ungültiger Jobject. Das ist der Fehler, den ich bekomme, wenn ich versuchte, den "Rückruf" – Winster

Antwort

2

(2) Zuerst müssen Sie einen Verweis auf JavaVM speichern, so dass Sie in der Lage sein werden, JNIEnv später von anderen Thread zu erhalten. Außerdem müssen Sie eine neue globale Referenz von der lokalen Variable erhalten, die von Parameter erhalten wurde (vergessen Sie nicht, es zu löschen, wenn es nicht mehr benötigt wird, oder es wird Speicherverlust verursachen).

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) Bei Verwendung von Generika, nativen Seite kann nicht wissen, welche Art sie sind so überall Sie T verwenden Sie Object auf dem JNI/C/CPP Teil

verwendet werden sollen, Sie beginnen einen neuen Thread in C. Wenn Sie bereit sind, einen Callback von diesem Thread aus zu starten, müssen Sie ihn mit der Java Virtual Machine verbinden und anschließend trennen. Von was ich denke, Sie verwenden das Callback-Objekt nur einmal. In diesem Fall müssen Sie auch den globalen Verweis löschen.

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

aufzurufen Sorry, das hilft nicht. Natürlich cache ich das env; Ich dachte, es wird verstanden werden. Wie gesagt, das Problem, das ich hier zu lösen versuche, ist: wie Sie den Callback in Java aufrufen, wenn Sie die Instanz mit Interface als Typ haben. Das Aufrufen regulärer Klassenmethoden und das Aufrufen von ihnen aus einem nativen Thread sind einfach zu lösende Dinge. – Winster

+0

Sie können env nicht cachen, es ist nur für diesen Thread gültig (und für die Dauer des aktuellen jni Aufrufs?) Sie müssen es von jvm bekommen (Sie wussten wahrscheinlich schon, aber schrieb env anstelle von jvm). Lesen von Kommentar zu Ihrem Hauptbeitrag, ich sehe den Fehler: Verweis auf Projekt, das Sie von jni Anruf erhalten, ist lokal, und Sie müssen globale Ref vor dem Speichern für die spätere Verwendung machen –

+0

Ich aktualisierte den Code, wie ich jvm cache.Eine mögliche Option, die ich jetzt versuche, ist, den Rückruf nur in Java zu speichern. Aber ich muss das "jobject" von CPP an C übergeben. Weißt du wie es geht? Ich schätze, ein Zeiger kann mir hier helfen. – Winster