zoukankan      html  css  js  c++  java
  • JNI教程

    一、什么是JNI

    JNI(Java Native Interface ),它是Java SDK的一部分,主要用于实现Java对其他语言编写的代码和库的调用,比如C和C++。JNI提供的API也能让JVM嵌入其他的本地代码,实现本地代码对Java代码的调用。本教程主要讲解的就是Java和本地代码的互调,以及C/C++中的数据类型与Java中的数据类型的关系。

    二、需要的工具和库

    • Java编译器:javac.exe
    • Java虚拟机:java.exe
    • C头文件产生器:javah.exe
    • JNI的库文件(jvm.lib 或jvm.dll(.so))和头文件 (jni.h)
    • 一个C和C++编译器

    前面四个是JDK自带的放在%JAVA_HOME%in目录下面,最后一个工具根据你的平台不同有不同的选择。比如在Windows平台我们可以使用Mircosoft的编译器,在基于UNIX的系统下则可以使用GCC

    三、Java调用C++

    • 使用场景
      1. 对代码运行效率要求比较的,你可以采用稍底层更快的语言来编写,比如C/C++这样的本地代码(native code)。
      2. 你想要复用以前C/C++写的代码库
      3. 你需要一个平台相关的但是Java标准库又不支持的
    • 使用步骤

    通常我们完成Java调用C++需要以下6步

    1. 编写Java代码。这Java代码需要做3件事情:声明要调用的本地方法;加载包含了本地代码的共享库;调用本地方法。
    2. 编译Java代码。
    3. 创建C/C++头文件。这个C/C++头文件用于去声明将要调用的本地方法的签名,这个头文件使用C/C++去实现,并生产动态库。
    4. 编写C/C++的实现。实现第三步生成的头文件。
    5. 创建一个共享库。
    6. 运行Java代码。检测它是否正确工作。
    • 示例代码

    由于本示例只是测试Jni的调用,所以只写几个简单的方法。

    1. 编写Java代码

       1: public class Sample1
       2: {
       3:     public native int intMethod(int n);
       4:     public native boolean booleanMethod(boolean bool);
       5:     public native String stringMethod(String text);
       6:     public native int intArrayMethod(int[] intArray);
       7:     public static void main(String[] args)
       8:     {
       9:         System.loadLibrary("Sample1");
      10:         Sample1 sample = new Sample1();
      11:         int square = sample.intMethod(5);
      12:         boolean bool = sample.booleanMethod(true);
      13:         String text = sample.stringMethod("JAVA");
      14:         int sum = sample.intArrayMethod(
      15:         new int[]{1,1,2,3,5,8,13} );
      16:         System.out.println("intMethod: " + square);
      17:         System.out.println("booleanMethod: " + bool);
      18:         System.out.println("stringMethod: " + text);
      19:         System.out.println("intArrayMethod: " + sum);
      20:     }
      21: }

    PS:native关键字表示此方法由外部实现。

    2.编译Java代码

    javac

    java_file

    3.创建C/C++头文件

    javah

    javah_output

    头文件分析:

    javah_output_header

    JNIEXPORT:导出函数表示符号,JNICALL是调用约定(默认是stdcall),函数名是由Java前缀加类名在加方法名。jint,jboolean, jstring, jintarray和jobect是Java映射到C/C++中的类型,所有对应数据类型列表参加附录。JNIEnv:是Java虚拟机给我们提供的可调用的API。如下图

    image

    更多的常见jni.h

    PS:参数中的jobject形参传递的是一个当前类对象的引用

    4.编写C/C++的实现

       1: #include "Sample1.h"
       2: #include <string.h>
       3:  
       4: /*
       5:  * Class:     Sample1
       6:  * Method:    intMethod
       7:  * Signature: (I)I
       8:  */
       9: JNIEXPORT jint JNICALL Java_Sample1_intMethod
      10:   (JNIEnv * env, jobject obj, jint num)
      11: {
      12:     return num * num;
      13: }
      14:  
      15: /*
      16:  * Class:     Sample1
      17:  * Method:    booleanMethod
      18:  * Signature: (Z)Z
      19:  */
      20: JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
      21:   (JNIEnv * env, jobject obj, jboolean boolValue)
      22: {
      23:     return !boolValue;
      24: }
      25:  
      26: /*
      27:  * Class:     Sample1
      28:  * Method:    stringMethod
      29:  * Signature: (Ljava/lang/String;)Ljava/lang/String;
      30:  */
      31: JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
      32:   (JNIEnv * env, jobject obj, jstring strValue)
      33: {
      34:     const char* str = (*env)->GetStringUTFChars(env, strValue, 0);
      35:     char cap[128];
      36:     strcpy(cap, str);
      37:     (*env)->ReleaseStringUTFChars(env, strValue, str);
      38:     return (*env)->NewStringUTF(env, strupr(cap));
      39: }
      40:  
      41: /*
      42:  * Class:     Sample1
      43:  * Method:    intArrayMethod
      44:  * Signature: ([I)I
      45:  */
      46: JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
      47:   (JNIEnv * env, jobject obj, jintArray arrayValue )
      48: {
      49:     int i, sum = 0;
      50:     jsize len = (*env)->GetArrayLength(env, arrayValue);
      51:     jint* data = (*env)->GetIntArrayElements(env, arrayValue, 0);
      52:     for (i = 0; i < len; ++i)
      53:     {
      54:         sum += data[i];
      55:     }
      56:     (*env)->ReleaseIntArrayElements(env, arrayValue, data, 0);
      57:     return sum;
      58: }

    5.创建一个共享库

    如果对使用cl命令生产动态库不熟悉,可以使用VS生成动态库

    complier

    image

    6.运行Java代码

    image

    四、C/C++调用Java

    • 使用场景
      1. 在开发Android平台的游戏时,也需要通过Jni来调用第三方平台SDK。
      2. 你想要去实现一个平台无关的代码。
      3. 你想在本地代码中使用Java编写的代码。
      4. 你想使用Java标准库带来的好处。
    • 使用步骤

    通常我们完成C/C++调用Java需要以下4步

    1. 编写Java代码。使用Java代码去完成我们需要完成的功能
    2. 编译Java代码。 将编写好的Java代码生成字节码。
    3. 编写C/C++代码。用于去调用我们Java编写的代码
    4. 运行本地代码。
    • 示例代码

    1. 编写Java代码,如下:

       1: import java.lang.String;
       2:  
       3: public class Sample2
       4: {
       5:     public Sample2(String strValue)
       6:     {
       7:         _strValue = strValue;    
       8:     }
       9:     public static int intMethod(int n)
      10:     {
      11:         return n*n;
      12:     } 
      13:  
      14:     public static boolean booleanMethod(boolean bool)
      15:     {
      16:         return !bool;
      17:     }
      18:  
      19:     public void SetString(String str)
      20:     {
      21:         _strValue = str;
      22:     }
      23:  
      24:     public String GetString()
      25:     {
      26:         return _strValue;
      27:     }
      28:  
      29:     private String _strValue ;
      30: }

    2. 编译Java代码

        image

    3. 编写C/C++代码

    这一步也是最关键的一步,因为这里牵涉到Jni API的调用,比如创建JVM,在JVM中查找类,查找方法和调用方法。更多的API可以参见jni.h头文件。

    所有Java的字节码都必须在JVM中执行,所有我们的第一件事情就是要创建JVM。如何创建呢?调用JDK提供的库文件(jvm.dll或jvm.so)中的函数。

       1: // CCallJava.cpp : Defines the entry point for the console application.
       2: //
       3:  
       4: #include "stdafx.h"
       5:  
       6: #include <jni.h>
       7: #include <string.h>
       8: #include <exception>
       9:  
      10: int _tmain(int argc, _TCHAR* argv[])
      11: {
      12:     //构造初始化参数
      13:     JavaVMOption options;
      14:     JavaVMInitArgs vm_args;
      15:     JNIEnv* env;
      16:     JavaVM* jvm;
      17:     long status;
      18:     jclass cls;
      19:     jmethodID mid;
      20:     jint square;
      21:     jboolean not;
      22:  
      23:     options.optionString = "-Djava.class.path=./../../";
      24:     memset(&vm_args, 0, sizeof(vm_args));
      25:     vm_args.version = JNI_VERSION_1_2;
      26:     vm_args.nOptions = 1;
      27:     vm_args.options = &options;
      28:  
      29:     //创建JVM
      30:     status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
      31:     if (status != JNI_ERR)
      32:     {    
      33:         cls = env->FindClass("Sample2");        
      34:         if (cls != 0 )
      35:         {    
      36:             //获取构造函数
      37:             jmethodID cstrctMid = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
      38:             jobject obj = 0;
      39:             if (cstrctMid != 0)
      40:             {
      41:                 jstring strValue = env->NewStringUTF("This is testing string.");
      42:                 obj = env->NewObject(cls, cstrctMid, strValue);
      43:             }
      44:             
      45:             mid = env->GetStaticMethodID( cls, "intMethod", "(I)I");
      46:             if (mid != 0 )
      47:             {
      48:                 square = env->CallStaticIntMethod(cls, mid, 5);
      49:                 printf("Result of intMethod: %d
    ", square);
      50:             }
      51:  
      52:             mid = env->GetStaticMethodID(cls, "booleanMethod", "(Z)Z");
      53:             if (mid != 0 )
      54:             {
      55:                 not = env->CallStaticBooleanMethod(cls, mid, 1);
      56:                 printf("Result of booleanMethod: %d
    ", not);
      57:             }
      58:  
      59:             mid = env->GetMethodID(cls, "SetString", "(I)V");
      60:             if (mid != 0)
      61:             {
      62:                 env->CallVoidMethod(obj, mid, 100);
      63:             }
      64:             
      65:             mid = env->GetMethodID(cls, "GetString", "()Ljava/lang/String;");
      66:             if (mid != 0)
      67:             {
      68:                 jstring str = (jstring)(env->CallObjectMethod(obj, mid));
      69:                 const char* strResult = env->GetStringUTFChars(str, false);
      70:                 printf("Result of GetString: %s
    ", strResult);
      71:             }
      72:         }
      73:         jvm->DestroyJavaVM();
      74:     }
      75:     getchar();
      76: }
      77:  

    Ps:

    (1)如果你的是64的JDK在变编译C++的时候务必选择x64位的程序。

    (2)在使用C++中使用java的非基础数据类型时,请注意在后面加上分号。例如:

    一个public String GetString()Java函数,对应的C++的签名则是这样的:"()Ljava/lang/String;"

    五、高级

    六、附录

    附录A:JNI的类型

    image

    image

    image

    附录B:JNI方法的签名的组成规则

    image

    注意:

    (1)class类型的最后一个分号不是分割符号,而是这个类型的结束标志。

    (2)必须使用(/)代替(.)来作为包之间的分割。

  • 相关阅读:
    避免数据脏读
    OGG配置文件中参数化的运用
    GoldenGate基于中间队列文件的初始化
    一次linux中毒,挖矿病毒
    goldengate新版本中查看日志读取点
    dlopen用法参数flag介绍
    gdb调试带参数和调试core
    在现有的git服务器上面创建新的repo
    Play Old Diablo 2 on macOS Catalina
    Odoo中的Environment对象
  • 原文地址:https://www.cnblogs.com/qilezaitu/p/4480621.html
Copyright © 2011-2022 走看看