JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统),nkdjni
JNI和NKD入门系列一,纯java工程下JNI的开发流程(mac系统),nkdjni
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方法,以及要加载的动态库
System.loadLibrary("jni-test");//加载动态库模块
而动态库的全名是
注意:“.”后面的是动态库的存储格式,在不同系统下都是不一样的,
windows下是.dll,linux下是.so,mac下是.jnilib。
动态库名称都以“lib”开头,而代码中声明加载的库名时,不用加“lib”。
两个native方法get和set,格式如上。
2、编译该java文件,并单独生成头文件
javac JniTest.java命令是编译文件,并生成class文件。
java JniTest。是是用javah命令对class文件生成头文件,就是JniTest.h。
JniTest.h的内容有注释是不可编辑的,但是这里面的很多内容之后都要用到,内容如下:
这里面有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中的两个方法,如下:
可以看见,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文件如下:
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
相关文章
- 暂无相关文章
用户点评