1.得到类Class。
在JNI中用jclass来表示类,jclass cls = env->GetObjectClass(obj);得到obj的类。
2.得到类的字段ID或方法ID。
jfieldID fid = env->GetFieldID(cls,"list","Ljava/util/ArrayList;");
第一参数为类名,第二参数为字段名,第三个字段类型标签。
3.通过字段ID得到字段值。
jobject list = env->GetObjectField(obj,fid);
Java VM 类型标签表见 Java VM Type Signatures。Java原始类型都有对应的标识,类则用“L fully-qualified-class;”,如"Ljava/util/ArrayList;"表示的是ArrayList,对数组则用“[type”,对象数组如ArrayList[] a;标识为”[Ljava/util/ArrayList;”。
Java VM Type Signatures
Signature |
Java Programming Language Type |
Z |
boolean |
B |
byte |
C |
char |
S |
short |
I |
int |
J |
long |
F |
float |
D |
double |
L fully-qualified-class; |
fully-qualified-class |
[ type |
type[] |
( arg-types ) ret-type |
method type |
类类型 |
L跟完整类名,如Ljava/lang/String; (注意:以L开头,要包括包名,以斜杠分隔,最后有一个分号作为类型表达式的结束) |
数组type[] |
[type,例如 float[]的签名就是[float,如果是二维数组,如float[][],则签名为[[float,(注意:这里是两个 [ 符号)。 |
方法 |
(参数类型签名)返回值类型签名,例如方法: float fun(int a,int b),它的签名为(II)F,(注意:两个I之间没有逗号!),而对于方法String toString(),则是()Ljava/lang/String;。 |
访问Java方法与访问字段类似。JNI执行符号方法查找是根据方法的名字和方法类型来确定的。Java中的方法名是UTF-8形式,指定类的构造器形式为。
获取构造函数的方法ID,构造函数名称固定为“
JNI使用方法签名来表示Java方法的返回值,如签名(I)V,表示一个整形参数,没有返回值的方法。方法签名的普通形式是 “(argument-types)return-type”。如程序中
jmethodID midGet = env->GetMethodID(clsArrayList,"get","(I)Ljava/lang/Object;");
得到的是ArrayList的对象的Object get(int )方法ID
Code Example3
#include "edu_hust_flyingbb_jniexample_HelloWord.h" /* * Class: edu_hust_flyingbb_jniexample_HelloWord * Method: print * Signature: ()V */ JNIEXPORT void JNICALL Java_edu_hust_flyingbb_jniexample_HelloWord_print (JNIEnv *env, jobject obj){ //先得到类 jclass cls = env->GetObjectClass(obj); //得到字段ID jfieldID fid = env->GetFieldID(cls,"list","Ljava/util/ArrayList;"); if(fid == 0){ printf("cann't find list Ljava/util/ArrayList field !\n"); } //得到字段 jobject list = env->GetObjectField(obj,fid);
//得到ArrayList类 jclass clsArrayList = env->GetObjectClass(list); //得到ArrayList类的size()方法ID jmethodID midSize = env->GetMethodID(clsArrayList,"size","()I"); //得到ArrayList类的get()方法ID jmethodID midGet = env->GetMethodID(clsArrayList,"get","(I)Ljava/lang/Object;");
int len = env->CallIntMethod(list,midSize);
for(int i=0 ;i<> jstring s= (jstring)env->CallObjectMethod(list,midGet,(jint)i); const char *chars = env->GetStringUTFChars(s,0); printf("%s ",chars); env->ReleaseStringUTFChars(s,chars); } } |
下面是从另一个地方看到的资料,留下来参考:
在C++中调用Java的方法一般分为五个步骤:初始化虚拟机、获取类、创建类对象、调用方法和退出虚拟机。
1) 初始化虚拟机。
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
int res;
//设置参数
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni";
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res >= 0)
{
//创建虚拟机成功
}
一个应用程序只需要一个虚拟机,但是每个线程需要自己的虚拟机运行环境。我们从一个虚拟机获取多个当前线程的运行环境,代码如下:
int result=0;
result=jvm->AttachCurrentThread( reinterpret_cast( &env ), 0 );
if(result>=0)
{
//获取运行环境成功
}
当线程退出时,需要释放本线程使用的运行环境。
jvm->DetachCurrentThread();
2) 获取类
在进行方法调用之前,需要先获取相应的类,类名称必须包括包名,其中的“.”用“/”代替。
jclass JavaClass;
JavaClass = env->FindClass("com/test/TestInterface");
if(JavaClass != 0)
{
//获取成功
}
3) 创建类对象
如果需要调用的方法静态方法,则可以跳过本步骤。反之,则需要构造该对象。构造对象是通过调用类的构造函数来实现的,构咱函数的方法声明为, GetMethodID方法的参数在下一步骤详细说明。
jobject obj;
jmethodID ctor;
ctor = env->GetMethodID(JavaClass,"","()V");
if(ctor != 0)//获取方法成功
{
obj = env->NewObject(JavaClass, ctor);
}
4) 调用方法
调用一个方法需要两个步骤:获取方法句柄和调用方法。
jmethodID methodID = env->GetMethodID( JavaClass, "setTest","(I)V");
if(methodID!=0)//获取方法成功
{
env->CallVoidMethod( obj, methodID,12);
}
GetStaticMethodID是用来获取静态方法的定义,GetMethodID则是获取非静态的方法定义。他们传入参数的参数依次为:类定义、方法名称和方法的定义,方法的定义可以用jdk中带的javap工具反编译class文件获取,其格式如下:
public void setTest(int inTest);
Signature: (I)V
Signature后面的内容就是方法的定义。
CallVoidMethod是对获取的方法进行调用,JNI接口中提供了一系列的同类方法,包括静态方法的调用函数(如:CallStaticXXXMethod)和非静态的方法(如:CallXXXMethod),其中XXX表示的不同方法返回类型,包括int、object等等。
5) 退出虚拟机
退出虚拟机调用方法如下:
jvm->DestroyJavaVM();
在JNI接口定义和众多文档中都说,只有最后一个线程退出时,该方法才会返回,但是我只用一个线程,调用该方法也无法返回。故此建议系统退出时执行该方法,或者整个程序退出时,让虚拟机自己释放。
[注意]:
Ø 在处理中文字符串时,需要注意Java的char是双字节的,采用Unicode编码,在和C++中的char转换时,需要用到系统API:WideCharToMultiByte和MultiByteToWideChar。
Ø 注意对运行环境中对象引用时的释放,以免引起内存泄漏。
jstring str;
wchar_t *w_buffer =(wchar_t *)env->GetStringChars(str,0);
env->ReleaseStringChars(str,(const unsigned short *)w_buffer);
http://leexh8382.blog.163.com/blog/static/5440902007620218785/