• java native interface JNI 调用Java方法


    在上一篇文章中介绍了JNI。以及java调用JNI。这篇讲一下 JNI调用java方法。

          通过使用合适的JNI函数,你能够创建Java对象,get、set 静态(static)和 实例(instance)的域,调用静态(static)和实例(instance)函数。

    JNI通过ID识别域和方法。一个域或方法的ID是不论什么处理域和方法的函数的必须參数。
    下表列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每一个函数接受(作为參数)域或方法的类,它们的名称,符号和它们相应返回的jfieldID或jmethodID。

    函数  描写叙述
    GetFieldID  得到一个实例的域的ID
    GetStaticFieldID  得到一个静态的域的ID
    GetMethodID 得到一个实例的方法的ID
    GetStaticMethodID 得到一个静态方法的ID

    构造一个Java对象的实例


    jclass cls = (*env)->FindClass(env, "Lpackagename/classname;");  //创建一个class的引用
    jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V");  //注意这里方法的名称是"",它表示这是一个构造函数。并且构造參数是double型的
    jobject obj = (*env)->NewObjectA(env, cls, id, args);  //获得一实例,args是构造函数的參数,它是一个jvalue*类型。

    首先是获得一个Java类的class引用 (*env)->FindClass(env, "Lpackagename/classname;");  请注意參数:Lpackagename/classname; 。L代表这是在描写叙述一个对象类型,packagename/classname是该对象耳朵class路径,请注意一定要以分号(;)结束!

    然后是获取函数的id。jmethodID id = env->GetMethodID(cls, "", "(D)V");  第一个是刚刚获得的class引用,第二个是方法的名称,最后一个就是方法的签名


    难理解的函数签名


    JNINativeMethod的定义例如以下:


       jclass cls = (*env)->FindClass(env, "Lpackagename/classname;");  //创建一个class的引用
       jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V");  //注意这里方法的名称是"",它表示这是一个构造函数。      并且构造參数是double型的
        jobject obj = (*env)->NewObjectA(env, cls, id, args);  //获得一实例。args是构造函数的參数,它是一个jvalue*类型。

    第一个变量name是Java中函数的名字。 
    第二个变量signature,用字符串是描写叙述了函数的參数和返回值 
    第三个变量fnPtr是函数指针。指向C函数。

     

    当中比較难以理解的是第二个參数。比如
    "()V"
    "(II)V"
    "(Ljava/lang/String;Ljava/lang/String;)V"


    实际上这些字符是与函数的參数类型一一相应的。
    "()" 中的字符表示參数。后面的则代表返回值。比如"()V" 就表示void Func();
    "(II)V" 表示 void Func(int, int);

     

    那其它情况呢?请查看下表:

    类型 符号
    boolean Z
    byte B
    char C
    short S
    int I
    long L
    float F
    double D
    void V
    object对象 LClassName;      L类名;
    Arrays [array-type        [数组类型
    methods方法 (argument-types)return-type     (參数类型)返回类型


    稍稍补充一下:

    1、方法參数或者返回值为java中的对象时,签名中必须以“L”加上其路径,只是此路径必须以“/”分开。自己定义的对象也使用本规则

    比方说 java.lang.String为“java/lang/String”,com.nedu.jni.helloword.Student为"Lcom /nedu/jni/helloword/Student;"

    2、方法參数或者返回值为数组类型时,请前加上[

    比如[I表示 int[],[[[D表示 double[][][]。即几维数组就加几个[

    在本地方法中调用Java对象的方法

    1、获取你须要訪问的Java对象的类:

    jclass cls = (*env)->GetObjectClass(env, obj);       // 使用GetObjectClass方法获取obj相应的jclass。 
    jclass cls = (*env)->FindClass(“android/util/log”) // 直接搜索类名,须要是static修饰的类。

    2、获取MethodID:

    jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…)。获取静态方法的ID使用GetMethdoID方法获取你要使用的方法的MethdoID

    其參数的意义: 
    env-->JNIEnv 
    cls-->第一步获取的jclass 
    "callback"-->要调用的方法名 
    "(I)V"-->方法的Signature, 签名同前面的JNI规则。

    3、调用方法:

    (*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , 调用静态方法

    使用CallVoidMethod方法调用方法。參数的意义: 
    env-->JNIEnv 
    obj-->通过本地方法穿过来的jobject 
    mid-->要调用的MethodID(即第二步获得的MethodID) 
    depth-->方法须要的參数(相应方法的需求,加入相应的參数)

     

    注:这里使用的是CallVoidMethod方法调用,由于没有返回值,假设有返回值的话使用相应的方法。在后面会提到。

    CallVoidMethod                   CallStaticVoidMethod
    CallIntMethod                     CallStaticVoidMethod
    CallBooleanMethod              CallStaticVoidMethod
    CallByteMethod                   CallStaticVoidMethod

    Jni操作Java的String对象

    从java程序中传过去的String对象在本地方法中相应的是jstring类型,jstring类型和c中的char*不同。所以假设你直接当做char*使用的话,就会出错。

    因此在使用之前须要将jstring转换成为c/c++中的char*,这里使用JNIEnv提供的方法转换。

    const char *str = (*env)->GetStringUTFChars(env, jstr, 0);
    (*env)->ReleaseStringUTFChars(env, jstr, str);

    这里使用GetStringUTFChars方法将传进来的prompt(jstring类型)转换成为UTF-8的格式,就行在本地方法中使用了。


    注意:在使用完你所转换之后的对象之后,须要显示调用ReleaseStringUTFChars方法。让JVM释放转换成UTF-8的string的对象的空间。假设不显示的调用的话。JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出。

    以下是Jni訪问String对象的一些方法:

    • GetStringUTFChars          将jstring转换成为UTF-8格式的char*
    • GetStringChars               将jstring转换成为Unicode格式的char*
    • ReleaseStringUTFChars    释放指向UTF-8格式的char*的指针
    • ReleaseStringChars         释放指向Unicode格式的char*的指针
    • NewStringUTF               创建一个UTF-8格式的String对象
    • NewString                    创建一个Unicode格式的String对象
    • GetStringUTFLength      获取UTF-8格式的char*的长度
    • GetStringLength           获取Unicode格式的char*的长度

    以下提供两个String对象和char*互转的方法:

    /* c/c++ string turn to java jstring */
    jstring charToJstring(JNIEnv* env, const char* pat)
    {
    jclass     strClass = (*env)->FindClass(env, "java/lang/String");
    jmethodID  ctorID   = (*env)->GetMethodID(env, strClass, "", "([BLjava/lang/String;)V");
    jbyteArray bytes    = (*env)->NewByteArray(env, strlen(pat));
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);
    jstring    encoding = (*env)->NewStringUTF(env, "UTF-8");
    return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
    }


    /* java jstring turn to c/c++ char* */
    char* jstringToChar(JNIEnv* env, jstring jstr)
    {       
        char* pStr = NULL;
        jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
        jstring    encode    = (*env)->NewStringUTF(env, "utf-8");
        jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
        jsize      strLen    = (*env)->GetArrayLength(env, byteArray);
        jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
        if (jBuf > 0)
        {
            pStr = (char*)malloc(strLen + 1);
            if (!pStr)
            {
                return NULL;
            }
            memcpy(pStr, jBuf, strLen);
            pStr[strLen] = 0;
        }
        env->ReleaseByteArrayElements(byteArray, jBuf, 0);
        return pStr;
    }

    事实上在JNI中调用java的特性和在java中区别不大都是同样思想,比方先找类。再找构造,再创建对象。然后就进行一系列操作...等等。

    參考:http://zhiweiofli.iteye.com/blog/1830321

    如有问题请留言,转载注明出处。

  • 相关阅读:
    [原创]用C++类实现单向链表的增删查和反转操作
    [原创]c语言中const与指针的用法
    [原创]大连sap vt 实习生面试经历
    Android studio 相关错误处理
    Java 判断整数方法
    Android 网络编程
    Android 基础篇(二)
    Android ListView 的基本应用,包括缓存
    Java重点识记
    Android基础篇(一)
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6869243.html
Copyright © 2020-2023  润新知