JNI开发C调用Java的方法和构造函数(三)

2023-05-16


前言

JNI的基本使用,C中调用Java的成员变量,成员属性,构造方法,方法


提示:以下是本篇文章正文内容,下面案例可供参考

一、JNI的上下文?

大家知道在使用javah 编译class文件时会生成以下JNI关键字,如下。

JNIEXPORT void JNICALL Java_clz_Register_helloWord
  (JNIEnv *, jobject);

1)JNIEnv
实际代表了Java环境,通过这个JNIEnv*指针,就可以对java断的代码进行操作,创建java对象,调用java对象的方法获取java对象的属性等。JNIEnv的指针会被JNI传人到本地方法的实现函数中来对java端的代码进行操作;
2)jobject obj
如果native方法不是static的话 ,**这个obj就代表这个nativa方法的实例
如果native方法是static的话 ,**这个obj就代表这个native方法的类的class的对象实例(static方法不需要实例,所以可以用来代表这个类的class对象)
3)JNIEXPORT和JNICALL都是JNI关键字,表示此函数是要被JNI调用的

二、C/C++调用Java

1)C/C++ 调用java很像java中的反射,在调用的过程中
GetFieldID/GetMethodID
GetStaticFieldID/GetStaticMethodID
下面看一个具体的方法
GetFieldID(jclass clazz ,const char* name , const char* sign)
方法的参数说明:
clazz:这个方法依赖的类对象的class对象
name:这个字段的名称
sign:这个字段的签名

2)调用Java代码签名问题
使用javap命令 javap -s -p xxx.class

数据类型签名
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
voidV
objectL/java/lang/XXX
Array以[k开头加上元素类型的签名 例如 int[] 签名是[I

3)实现Java调用native函数传入String在c中拼接返回给java使用,步骤如下

  • 编写java代码
package clz;

public class Register {
    static {
        System.load("/Users/xxx/CLionProjects/libso1/cmake-build-debug/libxpmsLib.dylib");
    }

    static native  String  setPassword(String  password);

    public static void main(String[] args) {
        Register register=new Register();
        System.out.println(register.setPassword("123456"));
    }
}
  • 编写c代码
JNIEXPORT jstring JNICALL Java_clz_Register_setPassword
        (JNIEnv * env, jobject j,jstring password){
//    printf(password);
    const char *c_p=NULL;
    jboolean  isCopy;
    c_p=(*env)->GetStringUTFChars(env,password,&isCopy);
    if(c_p==NULL){
        return NULL;
    }
    char  buf[128]={0};
    ///这里做字符串拼接
    sprintf(buf,"home%s",c_p);
    ///*c_p是指针类型 释放内存
    (*env)->ReleaseStringUTFChars(env,password,c_p);
    return (*env)->NewStringUTF(env,buf);
}

4)异常处理

if(c_p==NULL){
        return NULL;
    }

GetStringUTFChars之后需要安全检查,JVM需要对新诞生的字符串内存空间,当内存空间不够分配时,会导致调用失败,GetStringUTFChars调用失败后回返回NULL,并抛出OutOfMemoryError异常,java遇到异常程序会停止运行,JNI晕倒错误程序仍然会继续运行,后续程序针对该空字符串的操作都是危险的,需要return立即结束当前方法。

5)释放字符串
在调用GetStringUTFChars函数从JVM内部获取一个字符串后,JVM内部会分配一个内存存储源字符串的拷贝,以便本地代码的访问和修改,当使用完后需要调用ReleaseStringUTFChars释放,每一个GetXXX会对应一个ReleaseXXX

6)C访问java的成员变量

先找类–>fieldID–>获取值

  • java代码
package clz;

public class Register {
    static {
        System.load("/Users/xxx/CLionProjects/libso1/cmake-build-debug/libxpmsLib.dylib");
    }
    public int property=2;
    public static int staticProperty=1008;

    ///非静态方法 JNI中使用GetObjectClass(env,j)获取 class对象
    public native void getProperty();

    ///静态方法 JNI中使用 jobject 参数作为需要获取的class对象
    public static native void getStaticProperty();


    static native  String  setPassword(String  password);

    public static void main(String[] args) {
        Register register=new Register();
//        System.out.println(register.setPassword("123456"));
        register.getProperty();
        System.out.println(register.property);
        getStaticProperty();
        System.out.println(register.staticProperty);

    }
}
  • C代码

//获取非静态变量成员的数据

JNIEXPORT void JNICALL Java_clz_Register_getProperty(JNIEnv *env,jobject  j){
    //先取到类
    jclass clazz=(*env)->GetObjectClass(env,j);
    //先取到类ID
    jfieldID jfId=(*env)->GetFieldID(env,clazz,"property","I");
    //取值
    jint  jValue=(*env)->GetIntField(env,j,jfId);
    printf(" property jValue : %d \n",jValue);
}

//修改非静态变量成员的数据

//获取非静态变量成员的数据
JNIEXPORT void JNICALL Java_clz_Register_getProperty(JNIEnv *env,jobject  j){
    //先取到类  getProperty是非static的 所以可以使用 GetObjectClass 传入jobject 获取
    jclass clazz=(*env)->GetObjectClass(env,j);
    //先取到类ID
    jfieldID jfId=(*env)->GetFieldID(env,clazz,"property","I");
    //取值
    jint  jValue=(*env)->GetIntField(env,j,jfId);
    printf(" property jValue : %d \n",jValue);
    ///修改数据
  

//获取静态变量成员的数据 和修改数据

//获取静态变量成员的数据 和修改数据
JNIEXPORT void JNICALL Java_clz_Register_getStaticProperty(JNIEnv *env,jobject  j){
    //先取到类class getStaticProperty 是static 这里可以使用 FindClass获取 也可以直接只用jobject 参数
    jclass clazz=(*env)->FindClass(env,"clz/Register");
//    jclass clazz=(*env)->GetObjectClass(env,j);
    //先取到类ID
//    jfieldID jfId=(*env)->GetStaticFieldID(env,clazz,"staticProperty","I");///FindClass获取 clazz
    jfieldID jfId=(*env)->GetStaticFieldID(env,j,"staticProperty","I");//也可以直接只用jobject 参数
    //取值
    jint  jValue=(*env)->GetStaticIntField(env,j,jfId);
    printf("static property jValue : %d \n",jValue);
    ///修改数据
    (*env)->SetStaticIntField(env,j,jfId,11000);
}

7)JNI中FindClass和GetObjectClass的区别
FindClass是通过传java中完整的类名来查找java的class;
GetObjectClass是通过传入jni中的一个java的引用来获取该引用的类型。

8) JNI 调用 Java 类的方法(调用的是与native方法同一个类里的方法)
如果需要在JNI的C代码中调用Java方法,可以使用JNI提供的反射借口来反射得到Java方法,进行调用

  • Java代码
package clz;

public class Register {
    static {
        System.load("/Users/xxx/CLionProjects/libso1/cmake-build-debug/libxpmsLib.dylib");
    }
    public int property=2;
    public static int staticProperty=1008;

    ///非静态方法 JNI中使用GetObjectClass(env,j)获取 class对象
    public native void getProperty();

    ///静态方法 JNI中使用 jobject 参数作为需要获取的class对象
    public static native void getStaticProperty();


    static native  String  setPassword(String  password);

    ///测试JNI调用Java函数
    public native void testJINCallJavaMethod();

    public void methodTestJNICall(int a,String value ){

        System.out.println(" a == "+a+ " value ="+value);
    }

    public static void main(String[] args) {
        Register register=new Register();
//        System.out.println(register.setPassword("123456"));
//        register.getProperty();
//        System.out.println(register.property);
//        getStaticProperty();
//        System.out.println(register.staticProperty);
       register.testJINCallJavaMethod();
    }
}

  • C代码
///测试调用 Register 类中的 methodTestJNICall(int a,String value)方法
JNIEXPORT void  JNICALL Java_clz_Register_testJINCallJavaMethod(JNIEnv *env,jobject  obj){
    ///1 先获取TestJNICallJavaMethod类的class
    jclass  clazz=(*env)->FindClass(env,"clz/Register");
    if(clazz==NULL){
        return;
    }
    ///2 声明传入的string参数
    jstring jstr=(*env)->NewStringUTF(env," this is string");

    ///3 获取id jfId
    jmethodID jfId=(*env)->GetMethodID(env,clazz,"methodTestJNICall","(ILjava/lang/String;)V");
    if(jfId==0){
        return;
    }
    ///4 方法调用
    (*env)->CallVoidMethod(env,obj,jfId,11,jstr);
    (*env)->DeleteLocalRef(env,clazz);
    (*env)->DeleteLocalRef(env,jstr);
}

9)调用Java构造方法,并执行相应对象的实例方法

  • java代码
package clz;
//需要调用的构造方法/普通方法的类代码
public class TestJNICallJavaMethod {

    private  int property=3;
    TestJNICallJavaMethod(int property){
        this.property=property;
        System.out.println(" property == "+property);
    }

    public void methodTestJNICall(int a ,String value){

        System.out.println(" a == "+a+ " value ="+value);
    }

}

package clz;
///执行程序的入口调用
public class Register {
    static {
        System.load("/Users/xxx/CLionProjects/libso1/cmake-build-debug/libxpmsLib.dylib");
    }
    public int property=2;
    public static int staticProperty=1008;

    ///非静态方法 JNI中使用GetObjectClass(env,j)获取 class对象
    public native void getProperty();

    ///静态方法 JNI中使用 jobject 参数作为需要获取的class对象
    public static native void getStaticProperty();


    static native  String  setPassword(String  password);

    ///测试JNI调用本类的Java函数
    public native void testJINCallJavaMethod();

    ///测试JNI调用其他类的 Java函数
    public native void testOtherJINCallJavaMethod();

    public void methodTestJNICall(int a,String value ){

        System.out.println(" a == "+a+ " value ="+value);
    }

    public static void main(String[] args) {
        Register register=new Register();
//        System.out.println(register.setPassword("123456"));
//        register.getProperty();
//        System.out.println(register.property);
//        getStaticProperty();
//        System.out.println(register.staticProperty);
//       register.testJINCallJavaMethod();
        register.testOtherJINCallJavaMethod();
    }
}
  • C代码
///测试调用 TestJNICallJavaMethod 类中的 methodTestJNICall(int a,String value)方法
JNIEXPORT void  JNICALL Java_clz_Register_testOtherJINCallJavaMethod(JNIEnv *env,jobject  obj){
    ///1 先获取TestJNICallJavaMethod类的class
    jclass  clazz=(*env)->FindClass(env,"clz/TestJNICallJavaMethod");
    if(clazz==NULL){
        return;
    }
    ///2 声明传入的string参数
    jstring jstr=(*env)->NewStringUTF(env," this is string");

    ///3 获取 构造方法的 id jfId  这里传如的方法名称是 <init>
    jmethodID jfId=(*env)->GetMethodID(env,clazz,"<init>","(I)V");
    if(jfId==0){
        return;
    }
    /// 4.初始化构造函数(此处传递了参数)
    jobject jniObj=(*env)->NewObject(env,clazz,jfId,290);

    /// 5.获取 TestJNICallJavaMethod类的methodTestJNICall的jmethodID
    jmethodID jfIdOther=(*env)->GetMethodID(env,clazz,"methodTestJNICall","(ILjava/lang/String;)V");

    ///6 方法调用
    (*env)->CallVoidMethod(env,jniObj,jfIdOther,11,jstr);
    (*env)->DeleteLocalRef(env,clazz);
    (*env)->DeleteLocalRef(env,jstr);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JNI开发C调用Java的方法和构造函数(三) 的相关文章

随机推荐