aScarels

也许我们歌唱太阳,也被太阳歌唱着

0%

Native方法

native方法

文章介绍了native方法的大致概念,以及如何实现一个native方法

“A native method is a Java method whose implementation is provided by non-java code.”

native方法的概念

简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言在外部实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C”告知C++编译器去调用一个C的函数。

好处

外界环境交互

​ 这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
JVM(java虚拟机)支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre(运行环境)的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
Sun的解释器是用C实现的,这使得它能像一些普通的C一样与外部交互。jre大部分是用java实现的,它也通过一些本地方法与外界交互。例如:类java.lang.Thread 的 setPriority()方法是用java实现的,但是它实现调用的是该类里的本地方法setPriority0()。这个本地方法是用C实现的,并被植入JVM内部,在Windows 95的平台上,这个本地方法最终将调Win32 SetPriority() API。这是一个本地方法的具体实现由JVM直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被JVM调用。

坏处

对 Java 外部的调用通常不能移植到其他平台上,在 applet 中还可能引发安全异常。实现本地代码将使您的 Java 应用程序无法通过 100% 纯 Java 测试。但是,如果必须执行本地调用,则要考虑几个准则:

  1. 将您的所有本地方法都封装在单个类中,这个类调用单个 DLL。对于每种目标操作系统,都可以用特定于适当平台的版本替换这个 DLL。这样就可以将本地代码的影响减至最小,并有助于将以后所需的移植问题包含在内。
  2. 本地方法要简单。尽量将您的 DLL 对任何第三方(包括 Microsoft)运行时 DLL 的依赖减到最小。使您的本地方法尽量独立,以将加载您的 DLL 和应用程序所需的开销减到最小。如果需要运行时 DLL,必须随应用程序一起提供它们。

实现

通过外部DLL实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyNative
{
public void showParms( String s, int i, boolean b )
{
showParms0( s, i , b );//包装起来,将本地方法同代码的其余部分分隔开来
}
private native void showParms0( String s, int i, boolean b );//本地方法声明为private
static//加载包含本地方法实现的DLL
{
System.loadLibrary( "MyNative" );
}
{
System.load("C:\\Users\\Administrator\\Desktop\\com\\***.dll");//也有这样写的,直接调用地址
}
}

生成代码的头文件

1
2
3
javac MyNative.java(将 .java 编译为 .class) 
javah -jni
MyNative(生成 .h 文件)

产生的头文件如下图所示

1
2
3
4
5
6
7
8
/*
* Class: MyNative
* Method: showParms0
* Signature: (Ljava/lang/String;IZ)V
*/
//本地方法原型 本来产生的没有这一行
JNIEXPORT void JNICALL Java_MyNative_showParms0
(JNIEnv *, jobject, jstring, jint, jboolean);
Environment指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

```jobject```指向在此 Java 代码中以及变量类型的原型和其他声明

实例化的 Java 对象 MyNative 的一个句柄(Handle;来标识对象或者是项目的标识符)

```c
#include <stdio.h>
#include "MyNative.h"//包含头文件jni.h(JNI API,变量类型的原型以及其他声明)
//""与<>的区别""会优先再当前目录下查找,没有再找系统的include目录<>直接再include中找。
JNIEXPORT void JNICALL Java_MyNative_showParms0
//起名字 包名+类名+方法名
(JNIEnv *env, jobject obj, jstring s, jint i, jboolean b)
{
const char* szStr = (*env)->GetStringUTFChars( env, s, 0 );//GetStringUTFChars:用来根据 Java 字符串或 jstring 参数创建 C 字符串。这是必需的,因为在本地代码中不能直接读取 Java 字符串,而必须将其转换为 C 字符串或 Unicode。
printf( "String = [%s]\n", szStr );
printf( "int = %d\n", i );
printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") );
(*env)->ReleaseStringUTFChars( env, s, szStr );
}

有关转换 Java 字符串的详细信息,请参阅标题为 NLS Strings and JNI 的一篇论文。但是,jboolean 和 jint 值可以直接使用。

DLL文件的创建

loadLibrary加载路径是按照java.libary.path进行查询所以应该找到自己环境变量的path的值,把dll文件放在path中配置的第一个路径下。

参考

文章参考了IBM的一个文章 感觉介绍的很详细

百度百科JNI以前不怎么用百度百科的 但是这个的确很详细 也是改变了我对百度的印象。。。

IT 达人的自己实现一个Native方法对于如何实际操作很详细了,包括了本文没有涉及的利用VS生成dll的部分

本文对上述博客 网站所写的内容做了总结摘录了很大一部分内容

Welcome to my other publishing channels