一、什么是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%\bin目录下面,最后一个工具根据你的平台不同有不同的选择。比如在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代码

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

头文件分析:

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

更多的常见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生成动态库

6.运行Java代码

四、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代码

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\n", 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\n", 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\n", 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的类型

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

注意:

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

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

JNI教程的更多相关文章

  1. JNI教程与技术手册

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/52806794 本文出自: [HansChen的博客] 概述 对于JNI,有些童鞋在没 ...

  2. Android JNI 学习(二):JNI 设计机制

    本章我们重点说明以下JNI设计的问题,本章中提到的大多数设计问题都与native方法有关.至于调用相关的API的设计,我们会在后面进行介绍. 一.JNI接口函数和指针 native 代码通过调用JNI ...

  3. Android JNI 学习(一):JNI 简介

    JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作. ...

  4. JNI详解---从不懂到理解

    转载:https://blog.csdn.net/hui12581/article/details/44832651 Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 C ...

  5. Android Jni 调用

    Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 Chap 3:javah命令帮助信息... 16 Chap 4:用javah产生一个.h文件... 17 Chap5:j ...

  6. GitHub 优秀的 Android 开源项目(转)

    今天查找资源时看到的一篇文章,总结了很多实用资源,十分感谢原作者分享. 转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介 ...

  7. GitHub 优秀的 Android 开源项目

    转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介绍那些不错个性化的View,包括ListView.ActionBar.M ...

  8. android的快速开发框架集合

    出自:http://blog.csdn.net/shulianghan/article/details/18046021 1.Afinal  (快速开发框架) 简介:http://www.oschin ...

  9. GitHub 优秀Android 开源项目

    阅读目录 1.Xabber客户端 2.oschina客户端 3.手机安全管家 4.星座连萌 5.玲闹铃 6.魔乐盒 7.PWP日历 8.Apollo音乐播放器 9.夏普名片识别 10.高仿人人网 11 ...

随机推荐

  1. 7.26实习培训日志-Oracle SQL(二)

    Oracle SQL(二) 条件表达式 CASE 语句 或者DECODE 函数,两者均可实现 IF-THEN-ELSE 的逻辑,相比较而言,DECODE 更加简洁 SELECT last_name , ...

  2. cdce62005配置说明

    相关芯片手册可查阅:http://www.ti.com.cn/product/cn/cdce62005 需要使用的软件下载地址:http://www.ti.com.cn/cn/lit/sw/scac1 ...

  3. Unity3d 控制物体移动、旋转、缩放

    在Unity中通过利用 Input Manager(输入管理器)可以很简单的实现对一个物体进行移动.旋转.缩放操作. 演示代码: //通过虚拟轴控制物体移动.旋转.缩放 public class Mo ...

  4. Open-source Tutorial - Material Design for WPF UI

    安装 Material Design Themes 通过 NuGet 包管理器搜索自动安装 通过 NuGet 包管理器控制台手动安装 Install-Package MaterialDesignThe ...

  5. 实现one hot encode独热编码的两种方法

    实现one hot encode的两种方法: https://stackoverflow.com/questions/37292872/how-can-i-one-hot-encode-in-pyth ...

  6. 浅谈js模块加载方式(初级)

    1.简介:  前端模块化开发日渐鼎盛,如何将零散的插件或者是普通的js脚本文件统一管理及引用,是众多开发者共同的目标.本人是从事.net开发的,最近对前端的一些东西特别的感兴趣,也会尝试的夹杂一点自己 ...

  7. SQL——模糊查询

    前言 在这个大数据时代,我们都离不开对数据的增删改查,增加.删除.修改这些看似都是一步完成的事情,但是对于查询来说,好的查询SQL可以大大的减少系统内存运行时间,提高系统的反应速度.这里简单的介绍一下 ...

  8. css 实现三级联动菜单

    昨天因为项目中想要把二级联动菜单改成三级联动菜单,所以我就单独写了一个tab导航栏,用纯css的方式实现的三级联动.一开始我想着可以用js实现,但是js的hover事件和mouseenter,mous ...

  9. Mysql实例参数优化15个主要参数讲解(原创)

    1.innodb_buffer_pool_size 设置物理内存的60%-80%,反应IO吞吐的最大上限2.innodb_thread_concurrency 线程并发,设置为CPU核心数,如果等于0 ...

  10. Ckeditor 4 复制粘贴截图并转化base64格式保存至数据库

    虽然Ckeditor 中自带的有上传图片和文件的功能,但是有时候我们并不需要把图片保存至服务器的文件夹中. 反而是截图复制粘贴,把图片转化为base64格式保存在数据库中即可满足要求. 1.首先下载安 ...