JNI 基础 C语言版

作者: zcwfeng | 来源:发表于2020-08-06 09:10 被阅读0次

    静态注册和动态注册

    看下基本的jni的demo,我的java代码

    package zcwfeng;
    import java.*;
    
    public class Register{
       
        static{
            System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
        }
        public native String HelloWorld();
    
        public static void main(String[] args){
    
            Register r = new Register();
            String re =  r.HelloWorld();
            System.out.println(re);
    
        }
    }
    

    多说两句,就是可能引入jni.h 的时候不存在需要到java环境include里面jni头文件拷贝到项目,查找一下jni_md.h 也考过来,搜索一下就可以

    我用的clion环境创建的工程
    手动生成头文件

    zcwfeng_Register.h 头文件,生成的就补贴出来了
    zcwfeng_Register.c 实现,这个名字可以和头文件不保持一致,但是这样比较规范

    #include "zcwfeng_Register.h"
    #include <stdio.h>
    JNIEXPORT jstring JNICALL Java_zcwfeng_Register_HelloWorld(JNIEnv *env, jobject jobject1){
        return (*env)->NewStringUTF(env,"Hello David");
    }
    

    静态注册

    步骤:
    1)编写java类,假如是JniTest.java

    2)在命令行下输入 javac JniTest.java 生成JniTest.class文件

    1. 在 JniTest.class 目录下 通过 javah xxx.JniTest(全类名)生成 xxx_JniTest.h 头文件

    4)编写xxx_JniTest.c 源文件,并拷贝xxx_JniTest.h 下的函数,并实现这些函数,且在其中添加jni.h头文件;

    5)编写 cmakelist.txt 文件,编译生成动态/静态链接库

    动态注册

    在Register.java,加入动态native方法声明
    -> 动态注册三个方法声明
    native void dynamicFun1();
    native String dynamicFun2();
    native int getRandom();
    -> main 方法加入调用
    r.dynamicFun1();
    System.out.println(r.dynamicFun2());
    System.out.println(r.getRandom());

    为了方便,把动态注册的代码提取出来单独的c文件实现,dyReg.c

    #include "jni.h"
    
    void func1(JNIEnv *env, jobject jobject) {
        printf("dynamicNative1 动态注册");
    }
    
    
    jstring func2(JNIEnv *env, jobject jobject) {
        printf("dynamicNative2 动态注册");
        return (*env)->NewStringUTF(env, "Hello dynamic native");
    }
    
    jint func3(JNIEnv *env, jobject jobject) {
        return 100;
    }
    
    static const char * mClassName = "zcwfeng/Register";
    static const JNINativeMethod  mMethods[] = {
            {"dynamicFun1","()V",(void *)func1},
            {"dynamicFun2","()Ljava/lang/String;",(jstring *)func2},
            {"getRandom","()I",(jint *)func3}
    };
    
    -> 这部分方法参数和名字不用记,去jni.h 拷贝就行,也就是你java调用load 函数会自动调用这里
    JNIEXPORT jint JNICALL
    JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env = NULL;
        //获得 JniEnv
        int r = (*vm)->GetEnv(vm,(void **) &env, JNI_VERSION_1_4);
        if (r != JNI_OK) {
            return -1;
        }
        jclass mainActivityCls = (*env)->FindClass(env,mClassName); // 注册 如果小于0则注册失败
        r = (*env)->RegisterNatives(env,mainActivityCls, mMethods, 3);
        if (r != JNI_OK) {
            return -1;
        }
        return JNI_VERSION_1_4;
    }
    

    完整的java调用代码

    package zcwfeng;
    import java.*;
    
    public class Register{
        native void dynamicFun1();
        native String dynamicFun2();
        native int getRandom();
        -> 注意一下这里,windows 的库是dll,而mac里面是dylib
        static{
            System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
        }
        public native String HelloWorld();
    
        public static void main(String[] args){
    
            Register r = new Register();
            String re =  r.HelloWorld();
            System.out.println(re);
    
            r.dynamicFun1();
            System.out.println(r.dynamicFun2());
            System.out.println(r.getRandom());
        }
    }
    

    CMakeList 我的配置参考

    cmake_minimum_required(VERSION 3.15.3)
    project(TempC)
    
    set(CMAKE_CXX_STANDARD 11)
    
    -> 编译生成库,这里注释掉
    #add_executable(TempC zcwfeng_Register.c zcwfeng_Register.h dyReg.c src/main.cpp)
    add_library(firstlib SHARED zcwfeng_Register.h zcwfeng_Register.c dyReg.c)
    

    System.load 和 System.loadLibrary 的区别

    System.load
    System.load 参数必须为库文件的绝对路径,可以是任意路径,例如: System.load("C:\Documents and Settings\TestJNI.dll"); //Windows
    System.load("/usr/lib/TestJNI.so"); //Linux
    System.loadLibrary
    System.loadLibrary 参数为库文件名,不包含库文件的扩展名。 System.loadLibrary ("TestJNI"); //加载Windows下的TestJNI.dll本地库 System.loadLibrary ("TestJNI"); //加载Linux下的libTestJNI.so本地库
    注意:TestJNI.dll 或 libTestJNI.so 必须是在JVM属性java.library.path所指向的路径中。一般我们老程序员和喜欢是用命令的配置过java环境没问题
    -------------------------------------忧伤的分割线------------------------

    JNIEnv类型和jobject类型的解释

    -> 「这里JNIEXPORT 与 JNICALL 都是JNI的关键字,表示此函数是要被JNI调用的,无需过多解释」
    
    JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello
         (JNIEnv * env, jobject obj) {
         printf(hello);
    }
    

    JNIEnv* env参数的解释

    JNIEnv类型 实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作。
    例如,创建Java类中 的对象,调用Java对象的方法,获取Java对象中的属性等等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对 Java端的代码进行操作。如下代码所示

    #ifdef __cplusplus
    typedef JNIEnv_ JNIEnv;
    #else
    typedef const struct JNINativeInterface_ *JNIEnv;
    #endif
    ......
    struct JNIInvokeInterface_;
    ......
    struct JNINativeInterface_ {
        void *reserved0;
        void *reserved1;
        void *reserved2;
        void *reserved3;
        jint (JNICALL *GetVersion)(JNIEnv *env);
    //全是函数指针
    jclass (JNICALL *DefineClass)
          (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
           jsize len);
        jclass (JNICALL *FindClass)
          (JNIEnv *env, const char *name);
        jmethodID (JNICALL *FromReflectedMethod)
          (JNIEnv *env, jobject method);
        jfieldID (JNICALL *FromReflectedField)
          (JNIEnv *env, jobject field);
    jobject (JNICALL *ToReflectedMethod)
        (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
    ... }
    

    参数:jobject obj的解释

    如果native方法不是static的话,这个obj就代表这个native方法的类实例。
    如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例(static方法不需要类实例的,所 以就代表这个类的class对象)。
    java

    public native void test();
    public static native void testStatic();
    

    jni代码.h

    ->「JNIEnv *, jobject  代表()空参数」
    
    JNIEXPORT void JNICALL Java_Hello_test
      (JNIEnv *, jobject);
    JNIEXPORT void JNICALL Java_Hello_testStatic
      (JNIEnv *, jclass);
    

    C/C++代码调用Java代码

    在JNI中还有 一个非常重要的内容,那便是在C/C++本地 代码中访问Java端的代码,一个常见的应用就是获取类的属性和调用类的方法,为了在C/C++中表示属性和方法,JNI 在jni.h头文件中定义了jfieldId,jmethodID类型来分别代表Java端的属性和方法。
    我们在访问,或者设置Java属性的时候,首先就要先在本地代码取得代表该Java属性的jfieldID,然后才能在本地代码 中进行Java属性操作,同样的,我们需要呼叫Java端的方法时,也是需要取得代表该方法的jmethodID才能进行Java 方法调用。
    使用JNIEnv的:GetFieldID/GetMethodID GetStaticFieldID/GetStaticMethodID 来取得相应的jfieldID和jmethodID。下面来具体看一下这几个方法:

    GetFieldID(jclass clazz,const char* name,const char* sign) 方法的参数说明:
    clazz:这个简单就是这个方法依赖的类对象的class对象 name:这个是这个字段的名称 sign:这个是这个字段的签名(我们知道每个变量,每个方法都是有签名的)
    

    怎么查看类中的字段和方法的签名

    使用javap命令:

    javap -s -p JniTes.class
    

    java 字段的签名

    几个demo里的方法签名,也是常用的
    int->()I
    String->Ljava/lang/String;
    void->()V
    int [ ] ->[I
    float [ ]-> [F
    String [ ] -> [Ljava/lang/String;
    int [ ][ ] -> [[I
    float[ ][ ] -> [[F

    Java层方法                                               JNI函数签名
    
    String test ( )                                              Ljava/lang/String;
    int f (int i, Object object)                            (ILjava/lang/Object;)I
    void set (byte[ ] bytes)                                ([B)V
    
    
    对应于Java端的数据类型,我们也可以看一下下面的表: 
    Java 类型 类型签名
    boolean Z
    byte    B
    char    C
    short   S
    int     I
    long    L
    float   F
    double  D
    类   L全限定名;,比如String, 其签名为Ljava/lang/util/String;
    数组  [类型签名, 比如 [B
    

    JNI 访问字符串

    • java内部使用的是utf-16 16bit 的编码方式
    • jni 里面使用的utf-8 unicode编码方式 英文是1个字节,中文 3个字节
    • C/C++ 使用 ascii编码 ,中文的编码方式 GB2312编码 中文 2个字节
     public native static String sayHello(String text);
    
    ->JNI
    
    JNIEXPORT jstring JNICALL Java_JString_sayHello
            (JNIEnv * env, jclass jclaz, jstring jstr) {
        const char * c_str = NULL;
        char buf[128] = {0};
        jboolean  iscopy;
        c_str = (*env)->GetStringUTFChars(env, jstr, &iscopy);
        printf("isCopy:%d\n", iscopy);
        if(c_str == NULL) {
            return NULL;
    }
    printf("C_str: %s \n", c_str); sprintf(buf,"Hello, 你好 %s", c_str); printf("C_str: %s \n", buf); (*env)->ReleaseStringUTFChars(env, jstr, c_str); return (*env)->NewStringUTF(env,buf);
    }
    

    sayHello函数接收一个jstring类型的参数text,但jstring类型是指向JVM内部的一个字符串,和C风格的字符串类型 char*不同,所以在JNI中不能通把jstring当作普通C字符串一样来使用,必须使用合适的JNI函数来访问JVM内部的字 符串数据结构。

    异常处理

    if(c_str == NULL) {
            return NULL;
    }
    以上代码是很关键的,调用完GetStringUTFChars之后不要忘记安全检查,因为JVM需要为新诞生的字符串分配内存空
    间,
    当内存空间不够分配的时候,会导致调用失败,失败后GetStringUTFChars会返回NULL,并抛出一个 OutOfMemoryError异常。
    
    JNI的异常和Java中的异常处理流程是不一样的,
    Java遇到异常如果没有捕获,程序会立即停止 运行。
    而JNI遇到未决的异常不会改变程序的运行流程,也就是程序会继续往下走,这样后面针对这个字符串的所有操作都是非 常危险的,
    
    因此,我们需要用return语句跳过后面的代码,并立即结束当前方法。
    

    释放字符串

    在调用GetStringUTFChars函数从JVM内部获取一个字符串之后,JVM内部会分配一块新的内存,用于存储源字符串 的拷贝,以便本地代码访问和修改。即然有内存分配,用完之后马上释放是一个编程的好习惯。通过调用 ReleaseStringUTFChars函数通知JVM这块内存已经不使用了,你可以清除了。注意:这两个函数是配对使用的,用 了GetXXX就必须调用ReleaseXXX,而且这两个函数的命名也有规律,除了前面的Get和Release之外,后面的都一 样。

    访问Java 成员变量

    非静态变量

    java代码

     public int property;
    

    Jni代码

    JNIEXPORT void JNICALL Java_Hello_testField(JNIEnv *env, jobject jobj) {
    ->「首先调用GetObjectClass函数获取ClassField的Class引用」
        jclass  claz = (*env)->GetObjectClass(env,jobj);
    -> 「然后调用GetFieldID函数从Class引用中获取字段的ID(property是字段名,I是字段的签名)」
        jfieldID  jfid = (*env)->GetFieldID(env, claz, "property","I");
    -> 「最后调用GetIntField函数,传入实例对象和字段ID,获取属性的值」
        jint va = (*env)->GetIntField(env,jobj, jfid);
        printf("va: %d", va);
    -> 「最后调用GetIntField函数,传入实例对象和字段ID,获取属性的值」
        (*env)->SetIntField(env, jobj, jfid, va + 10086);
    }
    

    访问静态变量

    访问静态变量和实例变量不同的是,获取字段ID使用GetStaticFieldID,获取和修改字段的值使用
    Get/SetStaticXXXField系列函数,比如获取和修改静态变量num:

    num = (*env)->GetStaticIntField(env,clazz,fid); 
    // 4.修改静态变量num的值
     (*env)->SetStaticIntField(env, clazz, fid, 80);
    

    总结下

    1、由于JNI函数是直接操作JVM中的数据结构,不受Java访问修饰符的限制。即,在本地代码中可以调用JNI函数可以
    访问Java对象中的非public属性和方法
    
    2、访问和修改实例变量操作步聚:
    1>、调用GetObjectClass函数获取实例对象的Class引用 
    2>、调用GetFieldID函数获取Class引用中某个实例变量的ID 
    3>、调用GetXXXField函数获取变量的值,需要传入实例变量所属对象和变量ID 
    4>、调用SetXXXField函数修改变量的值,需要传入实例变量所属对象、变量ID和变量的值 
    
    3、访问和修改静态变量操作步聚: 
    1>、调用FindClass函数获取类的Class引用 
    2>、调用GetStaticFieldID函数获取Class引用中某个静态变量ID
    3>、调用GetStaticXXXField函数获取静态变量的值,需要传入变量所属Class的引用和变量ID 
    4>、调用SetStaticXXXField函数设置静态变量的值,需要传入变量所属Class的引用、变量ID和变量的值
    

    访问Java中的函数

    Java成员函数一般有两类:静态和非静态。所以在JNI中对这两种不同的类型就有了两种不太相同的调用方法,这两 种不同类型虽然他们的调用方式有些许不同,但是,他们的实质上是一样的。只是调用的接口的名字有区别,而对于 流程是没有区别的。

    private static void callStaticMethod(String str, int i) {
            System.out.format("ClassMethod::callStaticMethod called!-->str=%s," 
    +" i=%d\n", str, i);
    

    JNI

    JNIEXPORT void JNICALL Java_JniTest_callJavaStaticMethod(JNIEnv *env, jobject jobje){
    ->「1:从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象」
        jclass  clz = (*env)->FindClass(env,"ClassMethod");
        if(clz == NULL) {
            printf("clz is null");
            return; 
      }
    ->「2:从clazz类中查找callStaticMethod方法」
        jmethodID  jmeid = (*env)->GetStaticMethodID(env, clz, "callStaticMethod", "
    (Ljava/lang/String;I)V");
        if(jmeid == NULL) {
            printf("jmeid is null");
            return;
    }
    jstring arg = (*env)->NewStringUTF(env, "我是静态类");
    ->「2:从clazz类中查找callStaticMethod方法」
     (*env)->CallStaticVoidMethod(env,clz,jmeid,arg,100);
     (*env)->DeleteLocalRef(env,clz); 
    (*env)->DeleteLocalRef(env,arg);
    }
    

    JNI引用

    在 JNI 规范中定义了三种引用:局部引用(Local Reference)、全局引用(Global Reference)、弱全局引用
    (Weak Global Reference)。

    局部引用
    通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。会阻止GC 回收所引用的对象,不能本地函数中跨函数使用,不能跨线程使用。函数返回后局部引用所引用的对象会被JVM自动 释放,或调用DeleteLocalRef释放。 (*env)->DeleteLocalRef(env,local_ref)

    jclass cls_string = (*env)->FindClass(env, "java/lang/String");
    jcharArray charArr = (*env)->NewCharArray(env, len);
    jstring str_obj = (*env)->NewObject(env, cls_string, cid_string, elemArray);
    jstring str_obj_local_ref = (*env)->NewLocalRef(env,str_obj); // 通过NewLocalRef函数创建 ...
    

    释放一个局部引用有两种方式:
    1、本地方法执行完毕后VM自动释放;
    2、通过DeleteLocalRef手动释放; VM会自动释放局部引用,
    为什么还需要手动释放呢?
    因为局部引用会阻止它所引用的对象被GC回收。

    全局引用
    调用NewGlobalRef基于局部引用创建,会阻GC回收所引用的对象。可以跨方法、跨线程使用。JVM不会自动释放,
    必须调用DeleteGlobalRef手动释放 (*env)->DeleteGlobalRef(env,g_cls_string);

    tic jclass g_cls_string;
    void TestFunc(JNIEnv* env, jobject obj) {
        jclass cls_string = (*env)->FindClass(env, "java/lang/String");
        g_cls_string = (*env)->NewGlobalRef(env,cls_string);
    }
    

    弱全局引用
    调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使 用。引用不会自动释放,在JVM认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用 DeleteWeakGlobalRef手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)

    static jclass g_cls_string;
    void TestFunc(JNIEnv* env, jobject obj) {
        jclass cls_string = (*env)->FindClass(env, "java/lang/String");
        g_cls_string = (*env)->NewWeakGlobalRef(env,cls_string);
    }
    

    拼接字符串例子

    ->java
            public static native String sayHello(String ret);
    
    ->jni
    JNIEXPORT jstring JNICALL Java_zcwfeng_Register_sayHello(JNIEnv *env,jobject obj,jstring str){
    ->「将jstring转换成c可以用的char *」
        const char * c_str = NULL;
        jboolean isCopy;
        c_str = (*env)->GetStringUTFChars(env,str,&isCopy);
        if(c_str == NULL)
            return NULL;
    
    -> 1. malloc 2.初始化 3.释放----buf[128] = {0} 相当于1,2
        char buf[128] = {0};
    -> c里面的拼接,std::copy函数c++
        sprintf(buf,"Hello,你好:%s",c_str);
    -> 注意这里千万别忘记
        (*env) -> ReleaseStringUTFChars(env,str,c_str);
        return (*env)->NewStringUTF(env, buf);
    }
    
    

    获取Field实例

    package zcwfeng;
    public class FieldTest{
        static{
                System.load("/Users/zcw/dev/c_workspace/TempC/cmake-build-debug/libfirstlib.dylib");
        }
    
        public int property = 1;
        public static int statProperty = 8;
        public native void testField();
        public native void testStaticField();
    
        public static void main(String[] args){
    
                FieldTest t = new FieldTest();
                t.testField();
                t.testStaticField();
                System.out.println(t.property);
                System.out.println(t.statProperty);
    
            }
    
    }
    
    -> JNI
    JNIEXPORT void JNICALL Java_zcwfeng_FieldTest_testField(JNIEnv *env,jobject obj){
    //    (*env)->FindClass(env,)
        jclass clazz = (*env)->GetObjectClass(env,obj);
        jfieldID  jfid = (*env)->GetFieldID(env,clazz,"property","I");
        jint value = (*env)->GetIntField(env,obj,jfid);
        printf("java 原始值:%d",value);
        (*env)->SetIntField(env,obj,jfid,value + 10000);
    
    }
    
    
    JNIEXPORT void JNICALL Java_zcwfeng_FieldTest_testStaticField(JNIEnv *env,jobject obj){
    //    (*env)->FindClass(env,)
        jclass clazz = (*env)->GetObjectClass(env,obj);
        jfieldID  jfid = (*env)->GetStaticFieldID(env,clazz,"statProperty","I");
        jint value = (*env)->GetStaticIntField(env,obj,jfid);
        printf("java 原始值:%d",value);
        (*env)->SetStaticIntField(env,obj,jfid,value + 10000);
    
    }
    
    

    获取方法实例

    -> java 静态方法和非静态方法
    package zcwfeng;
    public class ClassMethod{
        private static void callStaticMethod(String str,int i){
            System.out.format("ClassMethod::callStaticMethod called!-str=%s, " + "i=%d\n",str,i);
        }
    
        private void callInstanceMethod(String str,int i){
            System.out.format("ClassMethod::callInstanceMethod called!-str=%s, " + "i=%d\n",str,i);
        }
    }
    
    -> JNI
    
    // 调用java构造方法
    JNIEXPORT void JNICALL Java_zcwfeng_Register_callJavaStaticMethod(JNIEnv *env,jobject obj){
        jclass jclz = (*env) -> FindClass(env,"zcwfeng/ClassMethod");
        if(jclz == NULL){
            printf("jclz is null");
            return;
        }
        jmethodID jconstructorId = (*env) -> GetMethodID(env,jclz,"<init>","()V");
        if(jconstructorId == NULL){
            printf("jconstructorId is null");
            return;
        }
    
        jobject jobj = (*env)->NewObject(env,jclz,jconstructorId);
        jmethodID jmid = (*env) -> GetStaticMethodID(env,jclz,"callStaticMethod","(Ljava/lang/String;I)V");
    
    
        jstring jstr = (*env)->NewStringUTF(env,"I am from JNI zcwfeng");
    
        -> 「注意一下这里CallStaticVoidMethod,参数是class而不是jobject」
        (*env)->CallStaticVoidMethod(env,jclz,jmid,jstr,1008600);
    
    
    
        (*env)->DeleteLocalRef(env,jclz);
        (*env)->DeleteLocalRef(env,jobj);
        (*env)->DeleteLocalRef(env,jstr);
    
    
    }
    
    // 调用java构造方法
    JNIEXPORT void JNICALL Java_zcwfeng_Register_callJavaInstanceMethod(JNIEnv *env,jobject obj){
        jclass jclz = (*env) -> FindClass(env,"zcwfeng/ClassMethod");
        if(jclz == NULL){
            printf("jclz is null");
        return;
        }
        jmethodID jconstructorId = (*env) -> GetMethodID(env,jclz,"<init>","()V");
        if(jconstructorId == NULL){
            printf("jconstructorId is null");
            return;
        }
    
        jobject jobj = (*env)->NewObject(env,jclz,jconstructorId);
        jmethodID jmid = (*env) -> GetMethodID(env,jclz,"callInstanceMethod","(Ljava/lang/String;I)V");
    
        jstring jstr = (*env)->NewStringUTF(env,"I am from JNI David\n");
        (*env)->CallVoidMethod(env,jobj,jmid,jstr,10086);
    
    
    
        (*env)->DeleteLocalRef(env,jclz);
        (*env)->DeleteLocalRef(env,jobj);
        (*env)->DeleteLocalRef(env,jstr);
    
    
    }
    
    
    

    野指针实例

    特别注意局部引用static的情况释放
    DeleteLocalRef
    并将对应的 引用智控
    区别什么是引用,GetMethodID方法不是

    -> java 本地方法
    
            public native String JCallC();
    
            public native String newString(int len);
            。。。
            Register r = new Register();
            r.JCallC();
            r.JCallC();   
            
    -> 逻辑是 JCallC()调用一个jni方法,jni内部调用newString方法        
    
    -> JNI 
    
    JNIEXPORT jstring JNICALL Java_zcwfeng_Register_newString(JNIEnv *env, jobject obj, jint len) {
    
        jcharArray elementArray;
        jstring j_str = NULL;
        static jclass cls_string = NULL;
        static jmethodID jmetid = NULL;
        if (cls_string == NULL) {
            printf("cls_string is null \n");
            cls_string = (*env)->FindClass(env, "java/lang/String");
            if (cls_string == NULL) {
                return NULL;
            }
        }
        if (jmetid == NULL) {
            printf("jmetid is null \n");
            jmetid = (*env)->GetMethodID(env, cls_string, "<init>", "([C)V");
            if (jmetid == NULL) {
                return NULL;
            }
        }
    
        printf("this is a line ----------------------- \n");
        elementArray = (*env)->NewCharArray(env, len);
        printf("this is a line2 ----------------------- \n");
        printf("this is a %d,%d,%d\n", cls_string, jmetid, elementArray);
        j_str = (*env)->NewObject(env, cls_string, jmetid, elementArray);
        printf("this is a line3 ----------------------- \n");
        (*env)->DeleteLocalRef(env, elementArray);
        elementArray = NULL;
    
        (*env)->DeleteLocalRef(env, cls_string);
        -> 「static静态引用,注释掉这句,下次在调用JCallC,就会导致野指针问题,因为开始判断为NULL,创建,现在没有=NULL,就会导致:指针指向的内存块内容为空」
        cls_string = NULL;
       -> 「这句是错的因为jmetid---GetMethodID 不是LocakRef 他不是引用类型」
    ////////    (*env)->DeleteLocalRef(env,jmetid);
        jmetid = NULL;
        printf("end of function\n");
        return j_str;
    
    }
    
    
    JNIEXPORT jstring JNICALL Java_zcwfeng_Register_JCallC(JNIEnv *env, jobject obj) {
        return Java_zcwfeng_Register_newString(env, obj, 1024);
    }
    
    

    相关文章

      网友评论

        本文标题:JNI 基础 C语言版

        本文链接:https://www.haomeiwen.com/subject/uywerktx.html