我们可以看到其中有四个函数声明, Java_完整类名_方法名, 完整类名包括了包名, 例如demo.Sample1是完整类名, 对应的这里就是demo_Sample1.

在注释中我们可以看到这样一个东西 Signature, 这个是方法的签名. 关于Signature, 下面通过一个表格来说明.

java类型

Signature

备注

boolean

Z


byte

B


char

C


short

S


int

I


long

L


float

F


double

D


void

V


object

L用/分割的完整类名

例如: Ljava/lang/String表示String类型

Array

[签名

例如: [I表示int数组,
[Ljava/lang/String表示String数组

Method

(参数签名)返回类型签名

例如: ([I)I表示参数类型为int数组, 返回int类型的方法

上面头文件的第一个函数声明

JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint);

注释中的签名是 Signature: (I)I

在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明.

实现头文件中的函数

可以使用C语言来实现, 也可以使用C++来实现, 下面先说说C语言的实现.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#include "Sample1.h"

#include <string.h>

JNIEXPORT jint JNICALL Java_Sample1_intMethod

(JNIEnv *env, jobject obj, jint num)

{

return num * num;

}

JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod

(JNIEnv *env, jobject obj, jboolean   boolean)

{

return !boolean;

}

JNIEXPORT jstring JNICALL Java_Sample1_stringMethod

(JNIEnv *env, jobject obj, jstring string)

{

const char* str = (*env)->GetStringUTFChars(env,   string, 0);

char cap[128];

strcpy(cap, str);

(*env)->ReleaseStringUTFChars(env,   string, 0);

return (*env)->NewStringUTF(env,   strupr(cap));

}

JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod

(JNIEnv *env, jobject obj, jintArray array)

{

int i, sum = 0;

jsize len =   (*env)->GetArrayLength(env, array);

jint *body =   (*env)->GetIntArrayElements(env, array, 0);

for (i = 0; i < len;   ++i)

{

sum +=   body[i];

}

(*env)->ReleaseIntArrayElements(env,   array, body, 0);

return sum;

}

(*env)->GetStringUTFChars()这个方法, 是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符, 所以需要进行转换.

JNIEnv *是每个函数都有的参数, 它包含了很多有用的方法, 使用起来类似Java的反射, 也提供了这样一个编码转换的函数.

GetStringUTFChars()和NewStringUTF(), 第一个是从UTF8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.

ReleaseStringUTFChars()是用来释放对象的, 在Java中有虚拟机进行垃圾回收, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.

函数的名字一眼看到就可以猜出功能, jni.h中的大部分函数名都是这样.

如果是C++的话, 这段代码该怎么写?

下面是C++的代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

#include "Sample1.h"

#include <string.h>

JNIEXPORT jint JNICALL Java_Sample1_intMethod

(JNIEnv *env, jobject obj, jint num)

{

return num * num;

}

JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod

(JNIEnv *env, jobject obj, jboolean   boolean)

{

return !boolean;

}

JNIEXPORT jstring JNICALL Java_Sample1_stringMethod

(JNIEnv *env, jobject obj, jstring string)

{

const char* str = env->GetStringUTFChars(string,   0);

char cap[128];

strcpy(cap, str);

env->ReleaseStringUTFChars(string,   0);

return env->NewStringUTF(strupr(cap));

}

JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod

(JNIEnv *env, jobject obj, jintArray array)

{

int i, sum = 0;

jsize len =   env->GetArrayLength(array);

jint *body =   env->GetIntArrayElements(array, 0);

for (i = 0; i < len;   ++i)

{

sum +=   body[i];

}

env->ReleaseIntArrayElements(array,   body, 0);

return sum;

}

上述两端代码非常相似, 只有一个不同点

C代码: (*env)->GetStringUTFChars(env, string, 0);

C++代码: env->GetStringUTFChars(string, 0);

C语言中使用的是结构体的函数指针, 而在C++中使用的还是struct, 我们知道struct在C++中和class的功能是几乎一样的,
struct也可以用来定义类, 所以env在C++中是个类对象的指针.

编译和运行

这里使用的是微软编译器, 编译C语言版的dll

>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.c -FeSample1.dll

编译C++版本的dll

>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.cpp -FeSample1.dll

运行

>java Sample1

注意: 64位版本的JDK可能会在运行时报错:

java.lang.UnsatisfiedLinkError: ...Sample1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

如果您有这样的错误, 请使用32位的JDK来重新运行.

运行结果如下:

intMethod: 25

booleanMethod: false

stringMethod: JAVA

intArrayMethod: 36

源代码下载: Sample1.zip

运行其中的build&run.bat文件即可, 如有错误请根据实际情况修改其中的一些参数.

DLL工程文件VC6.0和VS2010的: VC6.0&VS2010.zip

参考文献:

  1. Scott Stricker, 用 JNI 进行 Java 编程,
         http://www.ibm.com/developerworks/cn/education/java/j-jni/section2.html

  2. JDK 6u30 docs, Java Native      Interface Specification, Chapter 3 JNI Types and Data Structures, Type      Signatures.

JNI设置C++与java的结合(2)的更多相关文章

  1. Android中关于JNI 的学习(三)在JNI层訪问Java端对象

    前面两篇文章简介了JNI层跟Java层的一些相应关系,包含方法名,数据类型和方法名称等,相信在理论层面.可以非常好地帮助我们去了解JNI在Native本地开发中的作用,对JNI的一些概念也有了一个初步 ...

  2. Jni中C++和Java的参数传递 参数对照

    Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...

  3. Jni中C++和Java的参数传递

    Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...

  4. Jni中C++和Java的参数传递(转)

    如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用VC++6.0实现JNI的最简单的例子 ...

  5. Android JNI c/c++调用java 无需新建虚拟机

    近期通过研究SDL源码 得出android JNI  c/c++调用java 无需新建虚拟机: 具体步骤如下 第一步获得:两个参数 JNIEnv和jclass void Java_com_Test_A ...

  6. Linux上设置开机启动Java程序

    在Linux上设置开机启动Java程序,例如:test.jar 在Linux上启动Java程序的命令: nohup java -jar test.jar >/dev/>& & ...

  7. Android JNI中C和JAVA代码之间的互相调用

    关于Android studio中使用NDK/JNI环境和入门:http://blog.csdn.net/quan648997767/article/details/64923143 1. C代码回调 ...

  8. JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

  9. JNI字段描述符-Java Native Interface Field Descriptors

    一.JNI字段描述符 "[I" ---  int[] "[[[D" --- double[][][] 如果以一个L开头的描述符,就是类描述符,它后紧跟着类的字符 ...

随机推荐

  1. Switch控件详解

    Switch控件详解 原生效果 5.x 4.x 布局 <Switch android:id="@+id/setting_switch" android:layout_widt ...

  2. mysql进阶(二十九)常用函数

    mysql进阶(二十九)常用函数 一.数学函数 ABS(x) 返回x的绝对值 BIN(x) 返回x的二进制(OCT返回八进制,HEX返回十六进制) CEILING(x) 返回大于x的最小整数值 EXP ...

  3. Spring之MVC模块

    Spring MVC的Controller用于处理用户的请求.Controller相当于Struts 1里的Action,他们的实现机制.运行原理都类似 Controller是个接口,一般直接继承Ab ...

  4. Android Studio下多渠道打包

    Android Studio下实现多渠道打包 直接上步骤 步骤 1. 清单文件添加属性(以友盟统计为例) 在application标签下添加meta-data属性 <application -- ...

  5. activiti监听器使用

    分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_30739519) activiti使用的时候,通常需要跟业务紧密的结合在一起,有些业 ...

  6. HDFS的读数据过程分析

    我们继续在 FileSystem 类分析,读数据使用的是 open(-)方法,我们可以看到源码 FSDataInputStream in = fileSystem.open(new Path(&quo ...

  7. 混合开发(一)——WebView开发高级技巧之加载网页以及JavaScript,加载进度条

    混合开发(一)--WebView开发高级技巧之加载网页以及JavaScript,加载进度条 现在关于混合开发也越来越多了,很多人喜欢跟随,比如HB,比如RN,其实这东西很早就有这么一个概念了,而且说实 ...

  8. SpringMVC源码分析--容器初始化(三)HttpServletBean

    在上一篇博客springMVC源码分析--容器初始化(二)DispatcherServlet中,我们队SpringMVC整体生命周期有一个简单的说明,并没有进行详细的源码分析,接下来我们会根据博客中提 ...

  9. Ubuntu 12.04: How to enable root login

    1. vi /etc/lightdm/lightdm.conf and add following modifications. greeter-show-manual-login=true allo ...

  10. 在 root 下执行 Oracle 程序时找不到 libclntsh.so.11.1 错误的解决办法。

    在 root 下执行 Oracle 程序时找不到 libclntsh.so.11.1 错误的解决办法. 先确定 libclntsh.so.11.1 所在目录: [oracle@localhost ~] ...