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%\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)必须使用(/)代替(.)来作为包之间的分割。

时间: 2024-10-01 06:43:31

JNI教程的相关文章

最新版Android Studio 创建JNI教程

本文主要目的如题所示,并针对一些创建过程中可能会遇到的问题提供解决方案. 本文主要参考:http://ph0b.com/android-studio-gradle-and-ndk-integration/,里面有视频讲解,嫌看文档麻烦 的可以直戳进去... 主要思路:在一个类中创建native方法,编译生成该类的class文件,再去生成该类的头文件,有了头文件之后在c文件中编写代码的具体实现,配置sdk.ndk路径.给ndk模块起名,最后就是对该方法的调用. 详细步骤如下: 1.本地创建nati

传智播客 安卓 视频 教程

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha 传智播客 Android视频教程_传智播客和黑马程序员Android视频教程下载 step 01 初级教程 适用人群:有一定Java基础 学习周期:20天 市场价值初级Android工程师需求,薪资范围在6000~8000元/月. 学习后目标 1.能够根据项目需求,完成对应的模块设计与开发.2.能够担任初级Android工程师需求,可根据产品经理需求完成程序编写,多种分辨率的屏幕适配

Android JNI 学习(一):JNI 简介

JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作. JNI最重要的好处是它对底层Java VM的实现没有任何限制.因此,Java VM供应商可以添加对JNI的支持,而不会影响VM的其他部分.程序员可以编写一个native应用程序或库的版本,并期望它可以与支持JNI的所有Java VM一起使用. 本文主要将从以下几点讲述JNI相关的内容,用于我们了解J

优秀的 Android 开源项目

摘要  转载http://www.trinea.cn/android/android-open-source-projects-view/,方便大家找到自己合适的资料 目录[-] 一.ListView 二.ActionBar 三.Menu 四.ViewPager .Gallery 五.GridView 六.ImageView 七.ProgressBar 八.其他 GitHub上优秀Android开源项目 3. Android开发神器 1.Xabber客户端 2.oschina客户端 3.手机安全

GitHub 优秀的 Android 开源项目

转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介绍那些不错个性化的View,包含ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar及其它如Dialog.Toast.EditText.TableView.Activity Animation等等. 一.ListView android-pulltorefresh 一个强大的拉动

gitHub优秀android项目

转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介绍那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar及其他如Dialog.Toast.EditText.TableView.Activity Animation等等. 一.ListView android-pulltorefresh 一个强大的拉动

【转】GitHub 优秀的 Android 开源项目

转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介绍那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar及其他如Dialog.Toast.EditText.TableView.Activity Animation等等. 一.ListView android-pulltorefresh 一个强大的拉动

android开发开源宝贝——持续更新。。。

2016年11月11日更新 http://www.apkbus.com/forum-417-1.html http://p.codekk.com/detail/Android/hejunlin2013/LivePlayback www.codekk.com https://github.com/Trinea/android-open-project Android 开源项目分类汇总 我们的微信公众号:codekk.二维码如下: 专注于 Android 开源分享.源码解析.框架设计.Android

GitHub 优秀Android 开源项目

阅读目录 1.Xabber客户端 2.oschina客户端 3.手机安全管家 4.星座连萌 5.玲闹铃 6.魔乐盒 7.PWP日历 8.Apollo音乐播放器 9.夏普名片识别 10.高仿人人网 11.简洁天气 12.高仿开心网 13.百度推聊 14.餐厅订餐 15.eoe客户端 16.饭否网客户端 17.imiPhoneWall 18.k9mail 19.MSD音乐 20.高仿爱奇艺 21.企信通 22.高仿生日管家 23.PM25 24.高仿陌陌 25.股民邦 26.CB资讯阅读 27.幽秘