Android JNI编程规范

本文详细介绍了Android JNI编程规范,包括类型签名、数据类型、访问Java类的域和方法(包括私有域)、在native方法中使用自定义类、异常处理、类的操作、调用Java方法及访问Java对象的域等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的对象,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);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值