JNI初体验,JNIEnv类型分析与介绍

一、简单调用之java调用c代码:

1.首先创建一个java工程,写一个java类,里面写一个native方法,该方法返回一个字符串:

2. 生成头文件:找到工程的目录并进入bin目录:


复制这个路径,在命令提示符中进入这个路径(这个操作相信不用多说了,就是输入cd+空格+路径)


3. 进入刚才的路径并用javah命令生成.h头文件如下:

注意:javah后面跟的是刚才写的类的完整类名。之后回车,会看到bin目录下头文件已经生成:

4.将这个.h头文件复制到创建好的CPP工程中,这里我使用的编程工具是Visual Studio,如下图:

并选择   头文件--》添加--》现有项   添加到工程中打开:




打开这个文件之后发现有一些问题:

这个是没有关系的,只要系统中安装了JDK,在JDK安装目录中就有这个头文件。

5.在计算机java的安装目录下有一个include目录,目录下有该jni.h文件,同样复制到C工程中,步骤同上。


6.打开jni.h看到又引用了jni_md.h这个头文件:

7.上面说的include目录的win32目录里面有这个头文件,同样复制过去:


这个时候就都没有错误了。

8.在源.c中引用通过javah命令生成的头文件,并实现头文件中生成的那个与java中的native方法对应的方法:


9.源代码如下:

# include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include"jni_java_JniTest.h"

 

JNIEXPORT jstring JNICALL Java_jni_1java_JniTest_stringFromJni

(JNIEnv *env,jobjectjobject) {

    return (* env)->NewStringUTF(env,"java developer jalen.");

}

这里就是实现了头文件中定义的方法,通过NewStringUTF()函数构造了一个java的字符串并返回。

这里的NewStringUTF()函数就是创建一个jstring(一个在c中和Java的String类对应的字符串,也就是将C/C++的字符串转换为java的字符串。)

该方法是在jni.h中定义的。

10. 生成动态库文件(.dll文件,如果做Android开发生成.so文件,而so的生成需要电脑有Linux环境,比如安装Cygwin)提供给java工程使用,方法步骤如下:

1)  首先,电脑如果是windows64位的,需要指定生成的动态库的位数是64位:


第二步


2)  为工程指定生成文件为动态库文件(因为默认生成.exe可执行文件)

鼠标放在工程上右击—》属性—》配置属性—》常规—》配置类型


3)  最后生成,然后看到下面会有生成文件的目录


11.将这个目录添加到系统的环境变量的PATH变量中,这是因为在java代码中需要加载这个动态库,就要通过环境变量中配置的path路径去找,关于如何添加

环境变量这个太过基础,就不做讲解了。

Java代码中要写一个静态块加载刚才的.dll文件,注意不要在后面加.dll,只要写前面的名称就行了。

package jni_java;

 

publicclassJniTest {

  

   publicnative String stringFromJni();

 

   publicstaticvoid main(String[]args) {

      JniTestt =new JniTest();

      System.out.println(t.stringFromJni());

   }

   static{

      System.loadLibrary("Jalen");//加载动态库

   }

}

这里的静态块加载动态库文件,传入的参数是动态库的名字,所以要将动态库的路径添加到系统的环境变量中。如果添加到环境变量不能也不能识别路径,需要在工程的buildPath中添加:

或者直接将dll动态库文件复制到工程的根目录

12.运行的效果如下( 注意:如果改变了环境变量,需要先重启一下Eclipse

这里打印的这句话就是通过native方法调用C的实现代码而得到的。


说明:

1、  事实上,头文件里面只是有一些方法的定义而已,并没有方法的实现,所以不引用头文件也是可以的,但是行业规范要求我们这么做。生成的这个头文件相当于一个文档的作用,即我的java工程中有哪些方法是native方法都会在这个头文件中定义。

2、在C中的实现方法有JNIEXPORTJNICALL,这两个其实是头文件中的宏定义(关于宏定义以后的笔记中再作详细介绍,宏定义可以让程序员根据自己的编程习惯来编写代码),这个是可以去掉的,它不会对函数产生影响,相当于一个说明的作用。

 

对函数 

JNIEXPORTjstringJNICALL Java_jni_1java_JniTest_stringFromJni

(JNIEnv *env,jobjectjobject) {

return (* env)->NewStringUTF(env,"java developer jalen.");}

的说明:

1、  这个函数的函数名与要调用它的java类中的某个native方法对应。

2、  env是一个结构体指针的指针,而这个结构体就代表了jni编程的环境,也可以理解成Java和C/C++通信的一种规范。

因为JNIEnv类型为一个结构体指针类型,看定义如下图:

typedef的意义是对某一种类型取别名


3、  而这个结构体就是JNINativeInterface_  其定义的一部分如下:


后面还有很大的一部分,里面定义了大量的函数指针,其中NewStringUTF方法的指针也是这个结构体的一个成员,如下图是该函数指针在该结构体中的定义:


4、  这里说明一下结构体中的函数指针定义方法:返回类型 (方法名前面加*)(参数类型);

比如:int (*Hello)(int*,char*);  表示的就是 int hello(int * a , char * b){

}   这个方法的指针

5、  关于jstring,它表示的是Java的字符串(以后做详细介绍)。


这是本人第一次写博客,内容也是比较基础的,主要是自己学习的心得。希望对基础比较薄弱的童鞋有所帮助。当然,本人文中有任何错误之处请提出指正,谢谢啦!



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值