JNI 基础 - JNIEnv 的实现原理

作者: Peakmain | 来源:发表于2018-09-15 15:54 被阅读3次

    JNI的一般开发流程

    1.1 定义好本地的 native 方法

     public static void main(String[] args) {
            NdkSimple ndkSimple=new NdkSimple();
            String singnaturePassword = ndkSimple.getSingnaturePassword();
            System.out.println("秘钥=="+singnaturePassword);
        }
        //定义好本地的native方法
        public native String getSingnaturePassword();
    

    1.2 javah 命令生成 .h 头文件
    我用的是idea工具,在Terminal下首先定位到src目录下,然后在输入

    javah -jni com.example.demo.NdkSimple
    

    1.3 拷贝 xxx.h、jni_md.h、jni.h 到 VS 的工程目录并添加依赖进来


    image.png

    jni.h和jni_md.h到自己安装的jdk目录下搜索jni即可

    image.png

    1.4 实现我们头文件中的 native 方法

    #include "com_example_demo_NdkSimple.h"
    
    JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){
    
        return (*env)->NewStringUTF(env, "9931005");
    }
    

    1.5 生成 dll 动态,java 引入 dll 动态库运行即可
    代码写完后选择调试->最后一个选项xx属性,我的项目名字叫jni.


    image.png

    配置属性->常规->项目默认值->配置类型->选择dll选项


    image.png
    生成->配置项目管理器
    image.png
    平台->新建
    image.png

    我的是x64


    image.png

    生成->生成解决方案


    image.png

    java中引入动态库即可,动态库在自己的ndk项目x64目录下


    image.png
     static {
         //引入加载我们的动态库
         //System.loadLibrary():android中加载apk中的.so库
         //System.load()加载一个具体路径上的so库,去服务器下载再进行加载
         System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
     }
    

    详解.h头文件和实现文件

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include "jni.h"
    /* Header for class com_example_demo_NdkSimple */
    //用来打印一个标记,在c编译的时候会把头文件拷贝到你引入的地方,不管是重复引用还是相互引用都只会copy一次
    #ifndef _Included_com_example_demo_NdkSimple
    #define _Included_com_example_demo_NdkSimple
    #ifdef __cplusplus//相当于if语句 c++
    //不管是c还是c++统一都是采用c的编译方式,因为在c里面不允许函数重载的,但是c++里面是可以的
    extern "C" {
    #endif
    /*
     * Class:     com_example_demo_NdkSimple
     * Method:    getSingnaturePassword
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    
    #include "com_example_demo_NdkSimple.h"
    //JNIEXPORT JNI一个关键字,不能少。如果少了,编译能通过,但是运行出错。标记该方法可以被外部调用
    //jstring代表java中string
    //JNICALL 也是一个关键字,可以没有。jni call
    //JNIEnv:这个是java和c相互调用的桥梁
    //jobject:java传递下来的对象,就是本项目JniSimple.class对象
    //jclass:java传递下来的class对象,就是本项目JniSimple.class对象
    JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){
        return (*env)->NewStringUTF(env, "9931005");
    }
    

    c访问java属性

    首先java方法

    public class NdkSimple1 {
        static {
            //引入加载我们的动态库
            //System.loadLibrary():android中加载apk中的.so库
            //System.load()加载一个具体路径上的so库,去服务器下载再进行加载
            System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
        }
    
        private String name = "peakmain";
        public static int age = 18;
    
        public static void main(String[] args) {
            NdkSimple1 ndkSimple1 = new NdkSimple1();
            System.out.println("alert before:" + ndkSimple1.name);
            System.out.println("alert before:" + ndkSimple1.age);
            ndkSimple1.changeName();
            ndkSimple1.changeAge();
            System.out.println("alert after:" + ndkSimple1.name);
            System.out.println("alert after:" + ndkSimple1.age);
        }
    
        public native void changeName();
        public static native void changeAge();
    }
    

    生成头文件,获得类的签名文件如我的项目结构是,定位到test目录下,若不记得命令输入javap即可

    javap -p -s com.example.demo.NdkSimple1
    
    image.png

    我的结果图


    image.png

    c实现代码

    JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeName
    (JNIEnv *env, jobject jobj){
        //获取name属性,修改名字为Treasure
    
    
        // 3.获取 jclass 
        jclass j_clz = (*env)->GetObjectClass(env, jobj);
        // 获取 jfieldId (JNIEnv *env, jclass clazz, const char *name, const char *sig)
        // name 获取哪个属性的属性名 
        // 2.sig 属性的签名,**javap -p -s com.example.demo.NdkSimple1**
        jfieldID j_fid = (*env)->GetFieldID(env, j_clz, "name", "Ljava/lang/String;");
        // 1.获取 name 属性的值
        jstring j_str = (*env)->GetObjectField(env, jobj, j_fid);
        //打印字符串
        char* c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
    
        printf("name is %s", c_str);
        //修改名字为Treasure
        jstring TreaureName = (*env)->NewStringUTF(env, "Treasure");
    
        (*env)->SetObjectField(env, jobj, j_fid, TreaureName);
    }
    
    JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeAge
    (JNIEnv *env, jclass jclass){
        // 首先获取原来的
        jfieldID j_fid = (*env)->GetStaticFieldID(env, jclass, "age", "I");
        // Static 获取静态的
        jint age=(*env)->GetStaticIntField(env, jclass, j_fid);
        // jint -> int
        age += 6;
        // 设置新的 age 参数
        (*env)->SetStaticIntField(env, jclass, j_fid, age);
    }
    

    生成动态库即可

    c访问java方法

    添加方法

      public native void callAddMethod();
       public int add(int number1, int number2) {
            return number1 + number2;
        }
    

    c实现代码

    • 一般方法
    JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callAddMethod
    (JNIEnv *env, jobject jobj){
        jclass jclass=(*env)->GetObjectClass(env, jobj);
        //获取method
        jmethodID j_mid = (*env)->GetMethodID(env, jclass,"add" , "(II)I");
        //去调用java的方法
        jint sum = (*env)->CallIntMethod(env, jobj, j_mid, 2, 3);
        printf("sum = %d", sum);
    }
    
    • 静态常量方法
      java方法
     public static String getUUID() {
            return UUID.randomUUID().toString();
        }
        public static native void callGetUUID();
    

    c实现代码

    JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callGetUUID
    (JNIEnv *env, jclass jclz){
        // 2.获取 methodId
        jmethodID j_mid = (*env)->GetStaticMethodID(env, jclz, "getUUID", "()Ljava/lang/String;");
        // 1. 调用 getUUID 的方法 static 
        jstring j_uuid = (*env)->CallStaticObjectMethod(env, jclz, j_mid);
        //jstring->c_str
        char* c_uuid=(*env)->GetStringUTFChars(env, j_uuid, NULL);
        // 回收,字符串回收 
        //(*env)->ReleaseStringUTFChars(env, j_uuid, c_uuid);
        printf("c_uuid = %s", c_uuid);
    }   
    

    相关文章

      网友评论

        本文标题:JNI 基础 - JNIEnv 的实现原理

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