JNI

70 阅读2分钟
  1. 写一段调用native方法的java代码。

    package coroutinesLearning.jni;
    
    public class HelloWorld {
    
        // 声明一个native方法
        public native void sayHello();
    
        public static void main(String[] args) {
            new HelloWorld().sayHello();
        }
    
    }
    
  2. 使用javac生成一份头文件。

    #/path/to/HelloWorld.java换成HelloWorld.java文件的相对路径或绝对路径
    javac -h . /path/to/HelloWorld.java
    

    之后就能得到一个文件coroutinesLearning_jni_HelloWorld.h ,这名字是按照包名_类名.h格式得到的。

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class coroutinesLearning_jni_HelloWorld */
    
    /*注释1*/
    #ifndef _Included_coroutinesLearning_jni_HelloWorld
    #define _Included_coroutinesLearning_jni_HelloWorld
    
    /*注释2*/
    #ifdef __cplusplus
    extern "C" {//C模式的开始
    #endif
    /*
     * Class:     coroutinesLearning_jni_HelloWorld
     * Method:    sayHello
     * Signature: ()V
     */
     /*注释3*/
    JNIEXPORT void JNICALL Java_coroutinesLearning_jni_HelloWorld_sayHello
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }//C模式的结束
    #endif
    
    #endif
    
    • 注释1

      如果没定义_Included_coroutinesLearning_jni_HelloWorld,就定义_Included_coroutinesLearning_jni_HelloWorld,这是为了避免头文件被多次include,导致_Included_coroutinesLearning_jni_HelloWorld被重复定义。

    • 注释2

      这段代码的意识是告诉编译器,如果该头文件被C++文件include,则闭包部分(注释里用标注C模式的地方)按照C语言的规则来进行编译,这导致C模式下的代码实现不支持函数重载了,具体可看链接stackoverflow.com/a/3789553/1…

    • 注释3

      方法声明,不需要太关注JNIEXPORT和JNICALL两个符号,它两是定义在jni.h头文件的两个宏(标识符)。

  3. 实现native方法。

    创建HelloWorld.c文件。

    #include <jni.h>
    #include <stdio.h>
    #include "coroutinesLearning_jni_HelloWorld.h"
    
    JNIEXPORT void JNICALL Java_coroutinesLearning_jni_HelloWorld_sayHello(JNIEnv *env,jobject obj){
        printf("Hello from C!\n");
    }
    
  4. 编译C文件生成so文件。

    gcc -shared -o libhello.so -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin HelloWorld.c
    

    需要注意的是下述参数在可能在不同系统下会有变化,我的是macOS系统。

    -I${JAVA_HOME}/include/darwin
    
  5. 加载so库。

    在原来的HelloWorld.java文件里新增一段加载so库的代码

    package coroutinesLearning.jni;
    
    public class HelloWorld {
    
        static {
            //加载名为 "libhello" 的本地库
            //这里放so文件的相对或绝对路径
            System.load("/XXX/src/main/java/coroutinesLearning/jni/libhello.so"); 
        }
    
        // 声明一个native方法
        public native void sayHello();
    
        public static void main(String[] args) {
            new HelloWorld().sayHello();
        }
    
    }
    
  6. 运行。

    image.png