JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)

Java JNI 的本意是Java Native Interface (Java 本地接口),它是为了方便Java 调用C和

C++等本地代码所封装的一层接口。

我们都知道,Java 的优点是跨平台,但是作为优点的同时,其在和本地交互的时候就出现了短板。Java 的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java 无法完成,于是Java 提供了JNI专门用于和本地代码交互,这样就增强了Java 语言的本地交互能力。通过Java JNI,用户可以调用用C、C++所编写的本地代码。

 

NDK 则是Android 所提供的一个工具集合,通过NDK 可以在Android 中更加方便地通过JNI 来访问本地代码,比如C 或者C++。NDK 还提供了交叉编译器,开发人员只需要简单地修改mk 文件就可以生成特定CPU平台的动态库。

使用NDK 有如下好处:

(1) 提高代码的安全性。由于SO 库反编译比较困难,因此NDK 提高了Android 程序

的安全性。

(2) 可以很方便地使用目前已有的C/C++开源库。

(3) 便于平台间的移植。通过C/C++实现的动态库可以很方便地在其他平台上使用。

(4) 提高程序在某些特定情形下的执行效率,但是并不能明显提升Android 程序的

性能。

 

介绍完了基本概念,下面先来实现本章内容,java工程的JNI基本调用,一共分五步。


1、在java文件中中声明native方法,以及要加载的动态库

JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)_第1张图片

System.loadLibrary("jni-test");//加载动态库模块


而动态库的全名是

 

注意:“.”后面的是动态库的存储格式,在不同系统下都是不一样的,

windows下是.dll,linux下是.so,mac下是.jnilib。

动态库名称都以“lib”开头,而代码中声明加载的库名时,不用加“lib”。

 

两个native方法get和set,格式如上。

 

2、编译该java文件,并单独生成头文件

JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)_第2张图片

javac JniTest.java命令是编译文件,并生成class文件。

java JniTest。是是用javah命令对class文件生成头文件,就是JniTest.h

JniTest.h的内容有注释是不可编辑的,但是这里面的很多内容之后都要用到,内容如下:

JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)_第3张图片

这里面有JNi的语法,比如两个函数方法get和set,函数名遵守的规则的是Java_包名_类名_方法名(虽然我偷懒java文件没有包名)。这个规则有印象就好,不用记,因为这些靠javah命令就能自动生成。

解释几个关键词

JNIEXPORT JNIEXPORT:是jni.h中定义的宏;

JNIEnv*:表示指向JNI环境的指针,可以用来访问JNI提供的接口方法;

jobject:表示java 对象中的this;

String:  JNI中的类型,对应着java中的string

 

3、在c或c++文件中,实现JniTest.h中的两个方法,如下:

JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)_第4张图片

可以看见,test.c文件中的这两个方法的方法名和参数类型,是直接从之前的JniTest.h中复制过来的,我们给参数类型后面添加了参数名,并实现了内部的方法。

 

Jobject thiz不用this,是因为在java中this有特殊含义,如果在ide中,编译整个工程的话,后果严重你懂的。

#include"JniTest.h",包含了之前的java头文件(test.c和该头文件最好放同一目录下)。

 

几个JNI的接口方法定义如下:

(官方文档链接)

https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html#wp9502


jstring  NewStringUTF(JNIEnv *env, const char *bytes);//将string转为jstring类型

const  jbyte* GetStringUTFChars(JNIEnv *env, jstring string, 

jboolean*isCopy);//以utf格式返回一个数组指针

void  ReleaseStringUTFChars(JNIEnv *env, jstring string,

constchar *utf);//释放指针资源

 

4、通过gcc编译c或c++文件(我的例子是c),生成动态库(我的系统是mac,注意生成动态库后缀格式)如下:


 

-shares  -fPIC ,是生成动态库

-I  /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/include,是我的jdk路径

test.c,是c代码文件

-o libjni-test.jnilib,是编译要生成的文件名和格式(windows下是.dll,linux下是.so,mac下是.jnilib)

**********************************************补充说明***************************************

因为新换了台mac,用的是原生的mac的jdk,编译时会报错,信息如下:

//Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/jni.h:45:10: fatal error: 'jni_md.h' file not found
#include "jni_md.h"
         ^
1 error generated.

第一个解决的办法是添加一个 -I jdk路径/darwin 参数:
首先,简化jdk路径,用别名代替
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home
其次,在“4、”中的原命令基础上,添加-I jdk路径/darwin 参数
gcc -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin -fPIC test.c -o libjni-test.jnilib
这条命令中用别名JAVA_HOME简化了jdk路径,但是跟“4、”中原命令相比,只是多了个-I jdk路径/darwin 参数

第二个解决办法自然是去官网,重新下载jdk 安装,具体流程不做细讲。安装完成后可使用“4、”中的命令

**********************************************补充结束***************************************

 

5、指定动态库路径,执行java的class文件如下:

JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统)_第5张图片

 

ls命令,可见动态库文件libjni-test.jnilib在当前目录

Java  JniTest是执行已编译好的class文件

-Djava.library.path=. 指定了动态库所在目录,即当前目录,执行结果感人。

 

我在当前目录再建一个jni目录,并在jni目录下单独放一个动态库libjni-test.jnilib。

我再次执行的时候,-Djava.library.path=jni,修改了动态库的路径位置,结果同样感人。

 

完成

动态库文件在生成之后就是独立的模块,放在哪里都没问题,要修改相关功能,也只需要单独修改该模块,非常方便。

demo的源码链接如下:

https://github.com/wu2007369/-java-jni



你可能感兴趣的:(JNI,NDK,java,JNI,Mac,vim,gcc)