JNI开发流程-JNI/NDK【转】
本文转载自:http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/workflow.html
开发流程
JNI 全称是 Java Native Interface(Java 本地接口)单词首字母的缩写,本地接口就是指用 C 和 C++ 开发的接口。由于 JNI 是 JVM 规范中的一部份,因此可以将我们写的 JNI 程序在任何实现了 JNI 规范的 Java 虚拟机中运行。同时,这个特性使我们可以复用以前用 C/C++ 写的大量代码。
开发 JNI 程序会受到系统环境的限制,因为用 C/C++ 语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和 CPU 指令集,而且各个平台对标准 C/C++ 的规范和标准库函数实现方式也有所区别。这就造成使用了 JNI 接口的 JAVA 程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相应的动态库。
JNI 开发流程主要分为以下 6 步:
- 编写声明了 native 方法的 Java 类
- 将 Java 源代码编译成 class 字节码文件
- 用 javah -jni 命令生成
.h
头文件(javah 是 jdk 自带的一个命令,-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数) - 用本地代码实现
.h头
文件中的函数 - 将本地代码编译成动态库(Windows:\*.dll,linux/unix:\*.so,mac os x:\*.jnilib)
- 拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序
通过上面的介绍,相信大家对 JNI 及开发流程有了一个整体的认识,下面通过一个 HelloWorld 的示例,再深入了解 JNI 开发的各个环节及注意事项。
HelloWorld
注意:这个案例用命令行的方式介绍开发流程,这样大家对 JNI 开发流程的印象会更加深刻,后面的案例都采用eclipse+cdt 来开发。
第一步,新建一个 HelloWorld.java 源文件
public class HelloWorld {
public class HelloWorld {
public static native String sayHello(String name); // 1.声明这是一个native函数,由本地代码实现
public static void main(String[] args) {
String text = sayHello("yangxin"); // 3.调用本地函数
System.out.println(text);
}
static {
System.loadLibrary("HelloWorld"); // 2.加载实现了native函数的动态库,只需要写动态库的名字
}
}
第二步,用 javac 命令将.java
源文件编译成.class
字节码文件
注意:HelloWorld 放在 com.study.jnilearn 包下面
javac src/com/study/jnilearn/HelloWorld.java -d ./bin
-d 表示将编译后的 class 文件放到指定的目录下,这里我把它放到和 src 同级的 bin 目录下。
第三步,用 javah -jni 命令,根据class
字节码文件生成.h
头文件(-jni 参数是可选的)
javah -jni -classpath ./bin -d ./jni com.study.jnilearn.HelloWorld
默认生成的.h
头文件名为:com_study_jnilearn_HelloWorld.h(包名+类名.h),也可以通过-o
参数指定生成头文件名称:
javah -jni -classpath ./bin -o HelloWorld.h com.study.jnilearn.HelloWorld
参数说明:
- classpath:类搜索路径,这里表示从当前的 bin 目录下查找
- d:将生成的头文件放到当前的 jni 目录下
- o: 指定生成的头文件名称,默认以类全路径名生成(包名+类名.h)
注意:
-d
和-o
只能使用其中一个参数。
第四步,用本地代码实现.h头文件中的函数
- com_study_jnilearn_HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_study_jnilearn_HelloWorld */
#ifndef _Included_com_study_jnilearn_HelloWorld
#define _Included_com_study_jnilearn_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_study_jnilearn_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_study_jnilearn_HelloWorld_sayHello
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
- HelloWorld.c
// HelloWorld.c
#include "com_study_jnilearn_HelloWorld.h"
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Class: com_study_jnilearn_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_study_jnilearn_HelloWorld_sayHello(
JNIEnv *env, jclass cls, jstring j_str)
{
const char *c_str = NULL;
char buff[128] = { 0 };
c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
if (c_str == NULL)
{
printf("out of memory.\n");
return NULL;
}
(*env)->ReleaseStringUTFChars(env, j_str, c_str);
printf("Java Str:%s\n", c_str);
sprintf(buff, "hello %s", c_str);
return (*env)->NewStringUTF(env, buff);
}
#ifdef __cplusplus
}
#endif
第五步,将 C/C++ 代码编译成本地动态库文件动态库文件名命名规则:lib+动态库文件名+后缀(操作系统不一样,后缀名也不一样)如:
- Mac OS X : libHelloWorld.jnilib
- Windows :HelloWorld.dll(不需要 lib 前缀)
- Linux/Unix:libHelloWorld.so
1.Mac OS X
gcc -dynamiclib -o /Users/yangxin/Library/Java/Extensions/libHelloWorld.jnilib jni/HelloWorld.c -framework JavaVM -I/$JAVA_HOME/include -I/$JAVA_HOME/include/darwin
$JAVA_HOME目录在:/Library/Java/JavaVirtualMachines/jdk1.7.0_21.jdk/Contents/Home (可根据具体情况自己设置)
参数选项说明:
- -dynamiclib:表示编译成动态链接库
- -o:指定动态链接库编译后生成的路径及文件名
- -framework JavaVM -I:编译 JNI 需要用到 JVM 的头文件(
jni.h
),第一个目录是平台无关的,第二个目录是与操作系统平台相关的头文件
2.Windows (以 Windows7 下 VS2012 为例)
开始菜单-->所有程序-->Microsoft Visual Studio 2012-->打开 VS2012 X64 本机工具命令提示,用cl
命令编译成dll
动态库:
cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD HelloWorld.c -FeHelloWorld.dll
参数选项说明:
- -I :和 mac os x 一样,包含编译 JNI 必要的头文件
- -LD:标识将指定的文件编译成动态链接库
- -Fe:指定编译后生成的动态链接库的路径及文件名
3.Linux/Unix
gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared HelloWorld.c -o libHelloWorld.so
参数说明:
- -I: 包含编译JNI必要的头文件
- -fPIC: 编译成与位置无关的独立代码
- -shared:编译成动态库
- -o: 指定编译后动态库生成的路径和文件名
第六步,运行 Java 程序
Java 在调用 native (本地)方法之前,需要先加载动态库。如果在未加载动态之前就调用 native 方法,会抛出找不到动态链接库文件的异常。如下所示:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.study.jnilearn.HelloWorld.sayHello(Ljava/lang/String;)Ljava/lang/String;
at com.study.jnilearn.HelloWorld.sayHello(Native Method)
at com.study.jnilearn.HelloWorld.main(HelloWorld.java:9)
一般在类的静态(static)代码块中加载动态库最合适,因为在创建类的实例时,类会被 ClassLoader 先加载到虚拟机,随后立马调用类的 static 静态代码块。这时再去调用 native 方法就万无一失了。加载动态库的两种方式:
System.loadLibrary("HelloWorld");
System.load("/Users/yangxin/Desktop/libHelloWorld.jnilib");
方式1:只需要指定动态库的名字即可,不需要加lib
前缀,也不要加.so
、.dll
和.jnilib
后缀
方式2:指定动态库的绝对路径名,需要加上前缀和后缀
如果使用方式1,java 会去 java.library.path 系统属性指定的目录下查找动态库文件,如果没有找到会抛出java.lang.UnsatisfiedLinkError 异常。
Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloWorld2 in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:845)
at java.lang.System.loadLibrary(System.java:1084)
at com.study.jnilearn.HelloWorld.<clinit>(HelloWorld.java:13)
大家从异常中可以看出来,他是在 java.library.path 中查找该名称对应的动态库,如果在 Mac 下找libHelloWorld.jnilib 文件,linux 下找 libHelloWorld.so 文件,Windows 下找 libHelloWorld.dll 文件,可以通过调用 System.getProperties("java.library.path")方法获取查找的目录列表,下面是我本机mac os x 系统下的查找目录:
String libraryDirs = System.getProperty("java.library.path");
System.out.println(libraryDirs);
// 输出结果如下:
/Users/yangxin/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:
有两种方式可以让 Java 从 java.library.path 找到动态链接库文件,聪明的你应该已经想到了。
方式1:将动态链接库拷贝到java.library.path目录下
方式2:给 jvm 添加“-Djava.library.path=动态链接库搜索目录”参数,指定系统属性 java.library.path 的值 java -Djava.library.path=/Users/yangxin/Desktop Linux/Unix 环境下可以通过设置 LD_LIBRARY_PATH 环境变量,指定库的搜索目录。
运行写好的 Java 程序了,结果如下:
yangxin-MacBook-Pro:JNILearn yangxin$ java -classpath ./bin com.study.jnilearn.HelloWorld
Java Str:yangxin
hello yangxin
如果没有将动态库拷贝到本地库搜索目录下,执行java
命令,可通过添加系统属性 java.library.path 来指定动态库的目录,如下所示:
yangxin-MacBook-Pro:JNILearn yangxin$ java -Djava.library.path=/Users/yangxin/Desktop -classpath ./bin com.study.jnilearn.HelloWorld
Java Str:yangxin
hello yangxin
JNI开发流程-JNI/NDK【转】的更多相关文章
- JNI/NDK开发指南(一)—— JNI开发流程及HelloWorld
转载请注明出处:http://blog.csdn.net/xyang81/article/details/41777471 JNI全称是Java Native Interface(Java本地接口)单 ...
- JNI开发流程
交叉编译 在一个平台上去编译另一个平台上可以执行的本地代码 cpu平台 arm x86 mips 操作系统平台 windows linux mac os 原理 模拟不同平台的特性去编译代码 jni开发 ...
- NDK学习笔记-JNI开发流程
JNI(Java Native Interface)Java本地化接口,Java调用C/C++,C/C++调用Java的一套API接口 实现步骤 在Java源文件中编写native方法 public ...
- 编程基础知识——Java JNI开发流程(2)
android中使用jni调用本地C++库 android平台上的本地库文件后缀 .so.类似windows上的dll文件. 要在android上使用jni.首先须要下载android ndk. 操作 ...
- Android的JNI开发
变量的定义 int i; typedef int x;//定义一个int x数据类型 x a=10; printf("size=%d",sizeof(i));//获取int类型长度 ...
- JNI开发(2)——开发实战
JNI开发(1)--概述.环境搭建.必要知识点 JNI开发(2)--开发实战 本篇是重头戏:JNI实战开发.假设你对于 JNI.NDK 还没概念的话 那么观看本篇 也是没有太大难度的 ,哈哈哈哈! ! ...
- android下JNI开发
android下JNI开发 what 什么是JNI JNI java native interface native本地 java本地接口 通过JNI可以实现java和本地代码之间相互调用 jni可以 ...
- Android游戏开发实践(1)之NDK与JNI开发02
Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...
- Android jni简便开发流程
<Android jni helloworld>中介绍了开发jni helloworld的步骤,本文将介绍jni简便开发流程 ① 写java代码 native 声明本地方法 ② 添加本地支 ...
随机推荐
- 深入理解BootStrap之栅格系统(布局)
1.栅格系统(布局) Bootstrap内置了一套响应式.移动设备优先的流式栅格系统,随着屏幕设备或视口(viewport)尺寸的增加,系统会自动分为最多12列. 我在这里是把Bootstrap中的栅 ...
- channelartlist添加栏目链接
{dede:channelartlist} <a href='{dede:field name='typeurl'/}'></a> {/dede:channelartlist}
- 输出Java的GC信息
-verbose:gc -XX:+printGC 可以打印GC的简要信息 [GC 4790K->374K(15872K), 0.0001606 secs] [GC 4790K->374K( ...
- POJ1326问题描述
Description Mileage program of ACM (Airline of Charming Merlion) is really nice for the travelers fl ...
- 56个睿智帅气貌美的CTO大牛陪你叨逼叨
技术领域门槛高,苦读十年有时不如大牛一指点 可CTO级别的大牛何处找? 别急,别急 他们1月12日都去参加APICloud新品发布会了! (没图说啥子) 各路大牛们云集,不仅是为APICloud新品发 ...
- MVC 读书笔记
一.路由 1.HttpApplication中的ASP.NET MVC .Net 3.5 引入了System.Web.Routing程序集,通过Url Routing的机制,可以实现将一个虚拟路径的请 ...
- Android百度地图开发(一)之初体验
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/42614603 做关于位置或者定位的app的时候免不了使用地图功能,本人最近由于项目的需 ...
- 9G10内核时钟tick实现
9G10中PIT(Periodic Interval Timer)提供OS调度中断,它提供了最高精度和最有效的管理(即使系统长时间响应).一. 硬件PIT目标是提供OS的周期中断.PIT提供一个可编程 ...
- eclipse中运行Selenium遇到的问题
1. java.lang.NoClassDefFoundError: 解决方法:eclipse的java工程中导入selenium-java-2.44.0\selenium-2.44.0\libs ...
- ThinkPHP 自动验证与自动填充无效可能的原因(转)
自动验证与自动填充是在使用ThinkPHP时经常用到的功能,但偶尔会遇到自动验证与自动填充无效的情况,本文就ThinkPHP 自动验证与自动填充无效可能的原因做一些分析. create() Think ...