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


前言

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);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
数据采集卡采集的数据可以通过C语言的指针来表示。在C语言中,指针是一种变量类型,用于存储某个变量的内存地址。 要通过JNI将采集的数据转换为Java对象,你可以按照以下步骤进行操作: 1. 在C语言代码中,使用JNI提供的函数来创建Java对象。首先,需要在C代码中引入`jni.h`头文件,并使用`JNIEnv`指针来操作JNI函数。 2. 使用JNI函数`NewObject()`来创建Java对象。你需要提供Java类的引用(`jclass`),以及构造函数方法ID(`jmethodID`)。这样就可以在C代码中创建一个相应的Java对象。 3. 通过JNI函数`Set<Type>Field()`将采集的数据赋值给Java对象的字段。你需要提供Java对象的引用(`jobject`),字段的ID(`jfieldID`),以及将要设置的值。根据数据类型,可以使用不同的JNI函数来设置不同类型的字段。 4. 最后,将Java对象返回给Java层。可以通过JNI函数`Call<Type>Method()`调用Java层的方法,然后将创建好的Java对象作为返回值返回。 在Android软件开发中,你需要在Android Studio中创建一个JNI接口,并编写与C语言对应的JNI函数,用于与C代码进行交互。然后,在Java代码中调用JNI函数,将数据采集卡采集到的数据转换为Java对象。 请注意,JNI的使用需要一定的C语言Java语言的知识。确保你熟悉JNI的基本概念和语法,以便正确地进行数据转换和交互。 希望这些信息对你有所帮助!如果你需要进一步的指导,请提供更多的具体细节或代码示例,我将尽力提供更详细的建议。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shunsix

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值