Android JNI编程规范

来源:互联网 发布:网络攻防平台 编辑:程序博客网 时间:2024/05/22 01:34

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   byteC   charS   shortI   intJ   long F   floatD   doubleLjava/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的对象,argsClassB的构造函数的参数,它是一个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);
0 0