当前位置:首页 > 芯闻号 > 充电吧
[导读]  首先我实在 ubantu12.04 下进行操作的,还要有 eclipse ~~1.JNI的工作原理JNI : Java Native Interface 即JAVA本地调用(1)java的本质  

  首先我实在 ubantu12.04 下进行操作的,还要有 eclipse ~~

1.JNI的工作原理

JNI : Java Native Interface 即JAVA本地调用

(1)java的本质

  想搞明白jni的本质,还要从java的本质说起.从本质上来说,java这门语言就是一门脚本语言(这是偶的个人理解,希望java大侠们不要用板砖拍我),它的运行完全依赖于脚本引擎对java的代码进行解释和执行(当然了,现代的java已经先进许多,可以从源代码编译成.class之类的中间格式的二进制文件,这种处理会大大地加快java脚本的运行速度,但是基本的执行方式仍然不变,由脚本引擎(我们称之为JVM)来执行,与python、perl之类的纯脚本相比,它只是把脚本变成了二进制格式而已.另外就是java本身对面向对象的概念支持得很好,拥有完善的功能库可供调用,把这个脚本引擎移植到所有平台上,那么这个脚本自然就实现所谓的“跨平台”了).绝大多数的脚本引擎都支持一个很显著的特性,就是可以通过c/c++编写模块,在脚本中调用这些模块,以此来类比java,也是一样的,java一定要提供一种在脚本中调用c/c++编写的模块的机制,才能称得上是一个相对完善的脚本引擎.

  (2)android中的java

  android平台从本质上是 由arm-linux操作系统 和一个叫做dalvik的java虚拟机组成的.所有在android模拟器上面看到的那些华丽的界面,都是用java语言编写的(参见android平台源代码的frameworks/base目录).目前看来dalvik只是提供了一个标准的支持jni调用的java虚拟机环境.android平台中所有的硬件相关的操作均是采用jni技术进行了封装,由java去调用jni模块,而jni模块使用c/c++调用android本身的arm-linux底层驱动.

  例如,frameworks/base/libs/ui目录下面有一个叫做“EGLDisplaySurface.cpp”的文件,里面的:
  status_t EGLDisplaySurface::mapFrameBuffer()函数中,就有直接对android的arm-linux中的framebuffer的初始化代码.
  这也更加印证了,android其实是依靠java+jni建立起来的王国.hoho,如此一来,就凸显出jni在Android开发中的重要性(当然,一些简单的小程序是完全可以只用java就搞定的).
  “jni”的子目录,这个目录将用来存放.c的文件.


2.使用JNI两个原因

1、运行JAVA程序的虚拟机是用Native语言编写的,而虚拟机运行在具体的平台上,所以虚拟机本身无法做到平台无关,而利用JNI技术即可对JAVA层屏蔽不同操作系统平台之间的差异,如file,socket等
2、在JAVA语言诞生前,很多程序使用Native语言编写,JAVA直接利用JNI使用,避免造重复轮子的坏名声。而且JNI的运行效率和速度会更高


3.JNI 之 HelloWorld

  在Ubantu 中打开 eclipse ,写 java 代码:

HelloWorld.java


public class HelloWorld {

	static{
		System.loadLibrary("hello");
	}
	public native void DisplayHello();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new HelloWorld().DisplayHello();
		//System.out.println( System.getProperty("java.library.path"));
	}

}


进入src目录下,编译该JAVA类,(我是用默认包,不用写包名,有创建包的同鞋们要自己加包名)

命令:javac HelloWorld.java 在该 HelloWorld.java 所在目录下生成 HelloWorld.class 然后使用javah生成头文件, 命令:javah -jni HelloWorld 在当前目录下生成 HelloWorld.h 头文件,此文件供C、C++程序来引用并实现其中的函数

HelloWorld.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    DisplayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_DisplayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

注:1)、此头文件是不需要用户编译的,直接供其它C、C++程序引用。      2)、此头文件中的JNIEXPORT void JNICALL Java_HelloWorld_DisplayHello(JNIEnv *, jobject);方法,是将来与动态链接库交互的接口,并需要名字保持一致。

创建HelloWorld.cpp 实现头文件中的方法:

#include "jni.h"
#include "HelloWorld.h"
#includeJNIEXPORT void JNICALL Java_HelloWorld_DisplayHello
(JNIEnv *env, jobject obj)
{
    printf("From HelloWorld.cpp :");
    printf("Hello world ! n");
    return;
}

此C++文件实现了上述头文件中的函数,注意方法函数名要保持一致。

编译生成动态库libHello.so

编译c程序:
gcc -shared -fpic XXX.c -o XXX.so

编译c++程序:
g++ -shared -fpic XXX.cpp -o XXX.so


我是用下面这种方法:

g++ -I /usr/lib/jvm/java-6-sun/include/linux/ -I /usr/lib/jvm/java-6-sun/include/ -fPIC -c Hello.cpp

生成Hello.o

g++ -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0 Hello.o

生成libhello.so.1.0

接下来将生成的共享库拷贝为标准文件名

cp libhello.so.1.0 libhello.so

或者使用:

g++ -I /usr/lib/jvm/java-6-sun/include/linux/ -I /usr/lib/jvm/java-6-sun/include/ -fPIC -shared -o libLexical.so Lexical.cpp


编译好 .so接下来的任务是要让 java library path 能找到 .so
第一种方法:
现在HelloWorld.java 用 System.out.println( System.getProperty("java.library.path")); 打印出 java library 的路径, 然后把 libhello.so 复制到 java library path 的文件夹下面,如果没有权限的话用 sudo mv  ./x ...(我是用这种方法)

第二种方法:
设置当前的 .so 的路径为 java library path 可以访问的路径
export LD_LIBRARY_PATH=...../(路径):$LD_LIBRARY_PATH

设置好路径点运行成功
From HelloWorld.cpp :Hello world !

附:gcc 参数解释(转载):
   最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件

l -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真 正代码段共享的目的。

l -L.:表示要连接的库在当前目录中

l -ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称

l LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。

l 当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。

注意

1. 在linux下,动态链接库的名字必须是 lib****.so,必须以lib开头

2. 加载.so 文件时 ,我的c同事给我的.so 文件名为libswdes.so 我在java类里面调用时 需要这样写System.loadLibrary("swdes"); 不能带前面的lib和后缀名.so!

3. 调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Python和Java是两种广泛应用于编程领域的高级编程语言,它们各有优劣。本文从程序设计应用、系统资源占用、高性能处理和语言特点等四方面详细介绍两种编程语言的区别。

关键字: python java 高性能处理

在这篇文章中,小编将为大家带来Java接口的相关报道。如果你对本文即将要讲解的内容存在一定兴趣,不妨继续往下阅读哦。

关键字: java 接口 Interface

应该有不少小伙伴有假期打工的经历,那今天就来给公众号的读者朋友们分享一个二哥编程星球里一个球友在富士康打工 50 天的感受,相信大家看完后会深深触动的。

关键字: 物联网 java 博客

在过去的几年中,Python的普及速度令人惊叹,Java的王者地位也常年不衰。目前两者的竞争愈发激烈,怎么选择成为了许多初学者萦绕心中的问题,网上有很多版本的说法牵引着他们的思维,让本来很简单的问题复杂化。

关键字: python java

我们都知道,浏览一个网页,有两个很主要的“电脑”在共同运行,一台是远程的,为你提供网页数据的“服务器”,一台是你正在使用的客户端电脑。

关键字: javascript java

在各种网页制作技术论坛中,常常有人询问javascript与Java有什么区别,甚至有人误认为javascript就是Java。javascript与Java确实有一定的联系,但它们并不像我们想象的那样联系紧密,甚至可以...

关键字: javascript java

对于很多初学者来讲,不太清楚Java和C语言的区别,为了让那个大家更清晰的了解,近日特意给大家归纳了一些两者的大致区别,希望能够给大家带来一定的帮助作用,也欢迎大家进行详细补充和归纳。

关键字: C语言 java

Java封装案例

关键字: java 实例

今天我们就要来讲讲看似线程安全的双重检查锁单例模式中可能会出现的指令重排问题。

关键字: java 单例模式 双重检查锁

今天我们来放松下心情,不聊分布式,云原生,来聊一聊初学者接触的最多的 java web 基础。

关键字: Spring xml java
关闭
关闭