Demo
//增加学生信息:
addStu(JNIEnv *env,jobject valObj){
//获取jclass对象
jclass cls=env->GetObjectClass(valObj);
//GetFieldID方法用到取到jclas中的name字段。参数列表(jclass对象,字段名称,字段类型) 这点类似于java的反射
jstring name =(jstring)env->GetObjectField(valObj,env->GetFieldID(cls,"name","Ljava/lang/String;"));
jint age=(jint)env->GetObjectField(valObj,env->GetFieldID(cls,"age","I"));
jint sex =(jint)env->GetObjectField(valObj,env->GetFieldID(cls,"sex","I"));
//创建一个结构类型的对象 jstringToString方法用于把jstring类型转换成char *
student stu={jstringToString(env,name),(int)age,(int)sex};
//往向量的末尾增加一个对象
stus.push_back(stu);
}
//修改学生信息
upStu(JNIEnv *env,jobject obj,jobject objValue){
jclass cls=env->GetObjectClass(objValue);
jstring name=(jstring)env->GetObjectField(objValue,env->GetFieldID(cls,"name","Ljava/lang/String;"));
jint sex =(jint)env->GetObjectField(objValue,env->GetFieldID(cls,"sex","I"));
jint age=(jint)env->GetObjectField(objValue,env->GetFieldID(cls,"age","I"));
char * searchName =jstringToString(env,name);
for(int i=0;i<stus.size();i++){
student stu=stus.at(i);
if(strcmp(stu.name,searchName)==0){
stus.at(i).sex=(int)sex;
stus.at(i).age=(int)age;
}
}
}
//查询学生
getStu(JNIEnv *env,jobject obj,jstring str){
const char *nameStr=env->GetStringUTFChars(str,0);
jclass objectClass =(env)->FindClass("com/myjni/activity/Student");
jfieldID name =env->GetFieldID(objectClass,"name","Ljava/lang/String;");
jfieldID sex =env->GetFieldID(objectClass,"sex","I");
jfieldID age =env->GetFieldID(objectClass,"age","I");
for(int i=0;i<stus.size();i++){
student stu=stus.at(i);
if(strcmp(stu.name,nameStr)==0){
env->SetObjectField(obj,env->NewStringUTF(stus.at(i).name));
env->SetIntField(obj,sex,stus.at(i).sex);
env->SetIntField(obj,age,stus.at(i).age);
}
}
return obj;
}
//获取所有学生信息
getStus(JNIEnv *env,jobject obj){
class objClass =(env)->FindClass("java/lang/Object");
jobjectArray args = 0;
jsize len=stus.size();
args=env->NewObjectArray(len,objClass,0);
jclass objectClass =(env)->FindClass("com/myjni/activity/Student");
jfieldID name=(env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
jfieldID age=(env)->GetFieldID(objClass,"age","I");
jfieldID sex =(env)->GetFieldID(objClass,"sex","I");
for(int i=0;i<len;i++){
jobject tempObj =env->AllocObject(env->GetObjectClass(obj));
student stu=stus.at(i);
env->SetObjectField(tempObj,name,env->NewStringUTF(stus.at(i).name));
env->SetObjectField(tempObj,age,stu.age);
env->SetObjectField(tempObj,sex,stu.sex);
env->SetObjectArrayElement(args,i,tempObj);
}
return args; //返回的是数组对象
}
类型签名:
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Ljava/lang/String; String
[I int[]
数据类型
基本数据类型:
boolean byte char short int long float double --Java
jboolean jbyte jchar jshort jint jlong jfloat jdouble --jni
引用数据类型:
String Array[] Object --Java
String :
jstring是JNI对应于String的类型,但是和基本类型不同的是,jstring不能直接当做C++的string用。否则编译器会扔给你一个错误信息。
const char *str;
str =env->GetStringUTFChars(prompt,false); //先将java中的String转换成chars
if(str==null){
return null;
}
cout<<str<<endl; //可以进行字符数组的输出
env->ReleaseStringUTFChars(prompt,str); //注意释放存储,避免内存泄露
//如果要转换成string对象
char *tmpstr="returnStringSucceeded";
jstring str =env->NewStringUTF(tmpstr); //将char转换成jstring类型
总结:
将java中的String类型转换成JNI中de jstring类型
const char *chars=env->GetStringUTFChars(prompt,false);
jstring str =env->NewStringUTF(chars);
数组类型:
JNI为Java基本类型的数组提供了j*Array类型,比如int[] 对应的就是jintArray
JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env,jobject obj,jintArray arr){
jint *carr;
carr =env->GetIntArrayElements(arr,false);
if(carr==NULL){
return 0;
}
jintsum =0;
for(int i=0;i<10;i++){
sum+=carr;
}
env->ReleaseIntArrayElements(arr,carr,0);
return sum;
}
总结:
GetIntArrayElements和ReleaseIntArrayElements函数就死JNI提供用于处理int数组的函数。
二维数组和String数组:
在JNI中,二维数组和String数组都被视为object数组,因为数组和String被视为object。
JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,jclass cls,int size){
//因为要返回值,所以需要新建一个jobjectArray对象
jobjectArray result;
//创建一个jclass的引用
jclass intArrCls = env->FindClass("[I");
//为result分配空间
result =env->NewObjectArray(size,intArrCls,NULL);
for(int i=0;i<size;i++){
jint tmp[256]; //保证存储空间足够大
//为一维int数组iarr分配空间
jintArray iarr=env->NewIntArray(size);
for(int j=0;j<size;j++){
tmp[j]=i+j;
}
//为iarr赋值
env->SetIntArrayRegion(iarr,0,size,tmp);
//为result的第i个元素赋值
env->StObjectArrayElements(result,i,iarr);
env->DeleteLocalRef(iarr);
}
return result;
}
//创建一个二维int数组,并赋值完毕
注意:
以上使用的函数调用方式都是针对C++的,如果要用在C中,所有的env->都要被替换成(*env)->,而且后面的函数中需要增加一个参数env
访问Java类的域和方法 访问Java类的私有域和方法
public class ClassA{
String str="abcde";
int number;
public native void nativeMethod();
private void javaMethod(){
System.out.println("call javaMethod successed");
}
static{
System.loadLibrary("ClassA");
}
}
//C++实现
JNIEXPORT void JNICALL Java_testclass_ClassCallDLL_nariveMethod(JNIEnv *env,jobject obj){
//accessfield
jclass cls =env->GetObjectClass(obj); //得到对象
jfieldID fid=env->GetFieldID(cls,"str","Ljava/lang/String;");
jstring jstr =(jstring)env->GetObjectField(obj,fid);
const char *str=env->GetStringUTFChars(jstr,false);
if(std::string(str)=="abcde")
std::cout<<"accessfieldsuccesses"<<std::endl;
//得到字符字段
jint i=2468;
fid =env->GetFieldID(cls,"number","I");
env->SetIntField(obj,fid,i);
//设置值
jmethodID mid=env->GetMethodID(cls,"javaMethod","()V");
env->CallVoidMethod(obj,mid);
//调用不带参数的方法
}
在native方法中使用用户定义的类
使用自定义的类和使用Java的基础类(比如String)没有太大的区别,关键的一点是,如果要使用自定义类,首先要能访问类的构造函数。
jclass cls=env->FindClass("Ltestclass/ClassB"); //创建一个自定义类的引用
jmethodID mid=env->GetMethodID(cls,"<init>","(D)V"); //获得这个类的构造函数
jdouble dd=0.033;
jvalue args[1];
args[0].d=dd;
jobject obj=env->NewObjectA(cls,mid,args); //生成了一个ClassB的对象,args是ClassB的构造函数的参数,它是一个jvalue*类型。
异常处理
在native方法中发生了异常,传导到Java中
jclass Cls;
env->ExceptionDescribe();
env->ExceptionClear();
errCls=env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(errCls,"thrownfromC++code");
//如果要抛出其他类型的异常,替换掉FindClass的参数即可。这样,在Java中就可以接收native方法中抛出的异常。
类的相关操作
jclass FindClass(JNIEnv *env,const char *name); //查找类
常见异常:
ClassFormatError 类的数据格式无效
ClassCircularityError 该类或接口是自身的超类或超接口
NoClassDefFoundError 没有找到指定名称的类或接口
OOM 内存不足错误,即OutOfMemoryError
jclass GetSuperclass(JNIEnv *env,jclass clazz); //获取父类或者说超类
//第二个参数传入的是子类,否则返回将是NULL
jboolean IsAssignableFrom(JNIEnv *env,jclass clazz,jclass clazz2); //判断class1对象能否安全的强制转换为class2对象
/*
以下情况返回true: JNI_TRUE
1、这两个类参数引用同一个Java类
2、第一个类是第二个类的子类
3、第二个类是第一个类的某个接口
jclass GetObjectClass(JNIEnv *env,jobject obj); //通过对象获取这个类
//对象不能为NULL,否则获取的class肯定返回也为NULL
jboolean isInstanceOf(JNIEnv *env,jobject obj,jclass clazz); //判断对象是否为某一个类的实例
/*
注意:
返回值可能产生异议,就是如果传入的第二个参数为NULL对象,NULL对象可以强制转换为各种类,所有这种情况也将会返回JNI_TRUE,所以一定判断传入的对象是否为空。
*/
jboolean IsSameObject(JNIEnv *env,jobject ref1,jobject ref2); //判断两个对象是否引用同一个类
/*
如果两个对象均为空,返回的值也会是JNI_TRUE所以使用时判断对象为空
*/
调用Java方法:
jmethodID GetMethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig); //获取一个Java方法的ID
//这个函数将返回非静态类或接口实例方法的方法ID
/*
执行GetMethodID()函数将导致未初始化的类初始化,如果要获得构造函数的方法ID,使用<init>作为方法名,同时将void(V)作为返回类型,如果找不到指定的ID将返回NULL,同时异常可能有:
NoSuchMethodError 找不到指定的Java方法。
ExceptionInInitializerError 如果由于异常而导致类初始化程序失败
OutOfMemoryError 内存不足
*/
NativeType CallXXXMethod(JNIEnv *env,jobject obj,jmethodID methodID,va_list args); //调用XXX类型的Java方法
/*
执行Java类中的某个方法,需要注意的是这个里的java类是非静态的,由于Java的方法的类型比较多,所以该函数可能有以下几种形式,如:CallObjectMethod,CallBooleanMethod,CallByteMetod,CallCharMethod,CallShortMethod,CallIntMethod和CallVoidMethod,需要注意的是,该函数的第三个参数为通过GetMethodID函数获取的方法ID,最后一个参数为这个方法的参数表,最后的va_list宏可以通过搜索获取具体的使用方法。
*/
NativeType CallNonvirtualXXXMethod(JNIEnv *env,jobject obj,jclass clazz,jmethodID methodID,jvalue *args); //与CallXXXMethod()不同之处是多了一个jclass参数,CallXXXMethod()是根据对象来调用方法,而CallNonvirtualXXXMethod是根据类的实例调用。
上面的三个均为非静态类的获取,执行调用,需要实例化这个类才可以执行,下面的为静态调用。
jmethodID GetStatic MethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
NativeType CallStatic XXXMethod(JNIEnv *env,jclass clazz,jmethodID methodID,...);
访问Java对象的域
jfieldID GetFieldID(JNIEnv *env,jclass clazz,const char *name,const char *sig); //获取实例对象的域ID
/*
非静态的实例化后的对象,可能产生的异常有:
NoSuchFieldError 找不到指定的域
ExceptionInInitializerError 因为异常而导致类初始化失败
OutOfMemoryError内存不足
*/
NativeType GetXXXField(JNIEnv *env, jobject obj,jfieldID fieldID);
//类似GetXXXMethod函数,可能有的类型有 GetObjectField,GetBooleanField,GetByteField,GetCharField,GetShortField,GetIntField,GetLongField
void SetXXXField(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);
//Java的域可以赋值的,可能有的类型有 SetObjectField,SetBooleanField,SetByteField,SetCharField,SetShortField,SetIntField,SetLongField
上面3种情况均为非静态对象的域,对于不需要实例化对象的域,可以直接使用下面的
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
void SetStaticXXXField(JNIEnv *env, jclass clazz,jfieldID fieldID, NativeType value);