您的位置:首页 > 编程语言 > C语言/C++

JNI编程,C++调用Java

2014-07-15 14:11 507 查看


本地代码中使用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对象的实例

C代码  


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的定义如下:

C代码  


typedef struct {  

   const char* name;  

   const char* signature;  

   void* fnPtr;  

} JNINativeMethod;  

第一个变量name是Java中函数的名字。 

第二个变量signature,用字符串是描述了函数的参数和返回值 

第三个变量fnPtr是函数指针,指向C函数。

 

其中比较难以理解的是第二个参数,例如

"()V"

"(II)V"

"(Ljava/lang/String;Ljava/lang/String;)V"

实际上这些字符是与函数的参数类型一一对应的。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();

"(II)V" 表示 void Func(int, int);

 

那其他情况呢?请查看下表:
类型符号
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
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对象的类:

C代码  


jclass cls = (*env)->GetObjectClass(env, obj);       // 使用GetObjectClass方法获取obj对应的jclass。   

jclass cls = (*env)->FindClass(“android/util/log”) // 直接搜索类名,需要是static修饰的类。  


 


2、获取MethodID:

C代码  


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

其参数的意义: 

env-->JNIEnv 

cls-->第一步获取的jclass 

"callback"-->要调用的方法名 

"(I)V"-->方法的Signature, 签名同前面的JNI规则。

 


3、调用方法:

C代码  


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

使用CallVoidMethod方法调用方法。参数的意义: 

env-->JNIEnv 

obj-->通过本地方法穿过来的jobject 

mid-->要调用的MethodID(即第二步获得的MethodID) 

depth-->方法需要的参数(对应方法的需求,添加相应的参数)

 

注:这里使用的是CallVoidMethod方法调用,因为没有返回值,如果有返回值的话使用对应的方法,在后面会提到。

C代码  


CallVoidMethod                   CallStaticVoidMethod  

CallIntMethod                     CallStaticVoidMethod  

CallBooleanMethod              CallStaticVoidMethod  

CallByteMethod                   CallStaticVoidMethod  

现在稍稍明白文章开始构造Java对象那个实例了吧?让我们继续深入一下:

 


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/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;  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: