Search code examples
xamarin.androidjnienv

JNIEnv call from within monodroid application


This is the Java class i'm accessing through JNIEnv in my monodroid application

package mypackage;

import android.util.Log;

public class JavaScriptInterface {


    public String submitAns = "";

    // The JNI in the original question uses a default constructor.
    // Either provide one explicitly or use the implicit one...
    public JavaScriptInterface() 
    {

    }   

    public String getSelctd()
    {
        return submitAns;
    }
}

I'm able to instantiate the class by the following statements:

Java.Lang.Object jclassWrp_;

IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");

IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V"); //(Landroid/context/Context;)V

IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);

jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);

But when i try to create the object to access the getSelctd() method:

IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");

It throws NoSuchMethodExist Exception... Please tell me whether i'm doing it the right way and what i'm missing here...


Solution

  • I'm able to instantiate the class by the following statements:

    Java.Lang.Object jclassWrp_;
    IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");
    

    JNI use should use JNI conventions, thus mypackage/JavaScriptInterface (note / instead of .).

    IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V");
    IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);
    jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);
    

    JNIEnv.NewObject() returns a local ref, not a global ref, so you want JniHandleOwnership.TransferLocalRef.

    But when i try to create the object to access the getSelctd() method:

    IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");
    

    JNIEnv.GetMethodID() takes a class handle, not an instance. Firstly, the above shouldn't compile (Java.Lang.Object != IntPtr). Secondly, jclassWrp contains a mypackage.JavaScriptInterface instance, not the mypackage.JavaScriptInterface Class instance.

    Instead, do:

    IntPtr ipApid = JNIEnv.GetMethodID(JavaScriptInterface_Class, "getSelctd", "()Ljava/lang/String;");
    

    Finally, don't forget to JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class) when you don't need it anymore, otherwise you'll leak the gref.

    Complete code:

    // FindClass() returns a gref; must be freed (see below)
    IntPtr JavaScriptInterface_Class     = JNIEnv.FindClass("mypackage/JavaScriptInterface");
    
    // MethodIDs do not need to be freed
    IntPtr JavaScriptInterface_ctor      = JNIEnv.GetMethodID(JavaScriptInterface_Class,
            "<init>", "()V");
    IntPtr JavaScriptInterface_getSelctd = JNIEnv.GetMethodID(JavaScriptInterface_Class,
            "getSelctd", "()Ljava/lang/String;");
    
    // JNIEnv.NewObject() & JNIEnv.CallObjectMethod() return lrefs; freed below
    IntPtr lrefInstance                  = JNIEnv.NewObject(JavaScriptInterface_Class,
            JavaScriptInterface_ctor);
    IntPtr lrefSelectd                   = JNIEnv.CallObjectMethod(jsInterfaceinstance_, ipApid);
    
    // JniHandleOwnership.TransferLocalRef causes lrefSelectd to be released for us
    string selected                      = JNIEnv.GetString(lrefSelectd, JniHandleOwnership.TransferLocalRef);
    
    // Resource cleanup
    JNIEnv.DeleteLocalRef(lrefInstance);
    JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class);