按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
#endif
/*
* Class: ShowMsgBox
* Method: ShowMessage
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_ShowMsgBox_ShowMessage
(JNIEnv *; jobject; jstring);
#ifdef __cplusplus
}
#endif
#endif
从“#ifdef_cplusplus”这个预处理引导命令可以看出,该文件既可由C 编译器编译,亦可由C++编译器编
译。第一个#include 命令包括 jni。h——一个头文件,作用之一是定义在文件其余部分用到的类型;
JNIEXPORT 和JNICALL 是一些宏,它们进行了适当的扩充,以便与那些不同平台专用的引导命令配合;
JNIEnv,jobject 以及jstring则是JNI 数据类型定义。
2。 名称管理和函数签名
JNI 统一了固有方法的命名规则;这一点是非常重要的,因为它属于虚拟机将 Java 调用与固有方法链接起来
的机制的一部分。从根本上说,所有固有方法都要以一个“Java ”起头,后面跟随 Java 方法的名字;下划线
字符则作为分隔符使用。若Java 固有方法“过载”(即命名重复),那么也把函数签名追加到名字后面。在
原型前面的注释里,大家可看到固有的签名。欲了解命名规则和固有方法签名更详细的情况,请参考相应的
JNI 文档。
651
…………………………………………………………Page 653……………………………………………………………
3。 实现自己的 DLL
此时,我们要做的全部事情就是写一个C 或 C++源文件,在其中包含由 javah 生成的头文件;并实现固有方
法;然后编译它,生成一个动态链接库。这一部分的工作是与平台有关的,所以我假定读者已经知道如何创
建一个DLL。通过调用一个Win32 API,下面的代码实现了固有方法。随后,它会编译和链接到一个名为
MsgImpl。dll 的文件里:
#include
#include 〃ShowMsgBox。h〃
BOOL APIENTRY DllMain(HANDLE hModule;
DWORD dwReason; void** lpReserved) {
return TRUE;
}
JNIEXPORT void JNICALL
Java_ShowMsgBox_ShowMessage(JNIEnv * jEnv;
jobject this; jstring jMsg) {
const char * msg;
msg = (*jEnv)…》GetStringUTFChars(jEnv; jMsg;0);
MessageBox(HWND_DESKTOP; msg;
〃Thinking in Java: JNI〃;
MB_OK | MB_ICONEXCLAMATION);
(*jEnv)…》ReleaseStringUTFChars(jEnv; jMsg;msg);
}
若对Win32 没有兴趣,只需跳过MessageBox()调用;最有趣的部分是它周围的代码。传递到固有方法内部的
自变量是返回Java 的大门。第一个自变量是类型 JNIEnv 的,其中包含了回调JVM 需要的所有挂钩(下一节
再详细讲述)。由于方法的类型不同,第二个自变量也有自己不同的含义。对于象上例那样的非 static方法
(也叫作实例方法),第二个自变量等价于C++的“this ”指针,并类似于Java 的“this ”:都引用了调用
固有方法的那个对象。对于static方法,它是对特定 Class 对象的一个引用,方法就是在那个 Class 对象里
实现的。
剩余的自变量代表传递到固有方法调用里的Java 对象。主类型也是以这种形式传递的,但它们进行的“按
值”传递。
在后面的小节里,我们准备讲述如何从一个固有方法的内部访问和控制JVM,同时对上述代码进行更详尽的
解释。
A。1。2 访问 JNI 函数:JNIEnv 自变量
利用JNI 函数,程序员可从一个固有方法的内部与 JVM 打交道。正如大家在前面的例子中看到的那样,每个
JNI 固有方法都会接收一个特殊的自变量作为自己的第一个参数:JNIEnv 自变量——它是指向类型为
JNIEnv_的一个特殊 JNI 数据结构的指针。JNI 数据结构的一个元素是指向由 JVM 生成的一个数组的指针;该
数组的每个元素都是指向一个 JNI 函数的指针。可从固有方法的内部发出对JNI 函数的调用,做法是撤消对
这些指针的引用(具体的操作实际很简单)。每种 JVM 都以自己的方式实现了 JNI 函数,但它们的地址肯定
位于预先定义好的偏移处。
利用JNIEnv 自变量,程序员可访问一系列函数。这些函数可划分为下述类别:
■获取版本信息
■进行类和对象操作
■控制对Java 对象的全局和局部引用
■访问实例字段和静态字段
■调用实例方法和静态方法
■执行字串和数组操作
■产生和控制Java 异常
JNI 函数的数量相当多,这里不再详述。相反,我会向大家揭示使用这些函数时背后的一些基本原理。欲了
652
…………………………………………………………Page 654……………………………………………………………
解更详细的情况,请参阅自己所用编译器的JNI 文档。
若观察一下 jni。h头文件,就会发现在#ifdef _cplusplus 预处理器条件的内部,当由C++编译器编译时,
JNIEnv_结构被定义成一个类。这个类包含了大量内嵌函数。通过一种简单而且熟悉的语法,这些函数让我们
可以从容访问JNI 函数。例如,前例包含了下面这行代码:
(*jEnv)…》ReleaseStringUTFChars(jEnv; jMsg;msg);
它在C++里可改写成下面这个样子:
jEnv…》ReleaseStringUTFChars(jMsg;msg);
大家可注意到自己不再需要同时撤消对jEnv 的两个引用,相同的指针不再作为第一个参数传递给JNI 函数调
用。在这些例子剩下的地方,我会使用C++风格的代码。
1。 访问Java 字串
作为访问JNI 函数的一个例子,请思考上述的代码。在这里,我们利用 JNIEnv 的自变量jEnv 来访问一个
Java 字串。Java 字串采取的是Unicode 格式,所以假若收到这样一个字串,并想把它传给一个非 Unicode 函
数(如printf() ),首先必须用JNI 函数GetStringUTFChars()将其转换成 ASCII 字符。该函数能接收一个
Java 字串,然后把它转换成 UTF…8 字符(用 8 位宽度容纳 ASCII 值,或用 16 位宽度容纳 Unicode;若原始字
串的内容完全由ASCII 构成,那么结果字串也是ASCII)。
GetStringUTFChars 是JNIEnv 间接指向的那个结构里的一个字段,而这个字段又是指向一个函数的指针。为
访问JNI 函数,我们用传统的C 语法来调用一个函数 (通过指针)。利用上述形式可实现对所有JNI 函数的
访问。
A。1。3 传递和使用 Java 对象
在前例中,我们将一个字串传递给固有方法。事实上,亦可将自己创建的 Java 对象传递给固有方法。
在我们的固有方法内部,可访问已收到的那些对象的字段及方法。
为传递对象,声明固有方法时要采用原始的Java 语法。如下例所示,MyJavaClass 有一个 public (公共)字
段,以及一个public 方法。UseObjects 类声明了一个固有方法,用于接收 MyJavaClass 类的一个对象。为
调查固有方法是否能控制自己的自变量,我们设置了自变量的 public 字段,调用固有方法,然后打印出
public 字段的值。
class MyJavaClass {
public void divByTwo() { aValue /= 2; }
public int aValue;
}
public class UseObjects {
public static void main(String '' args) {
UseObjects app = new UseObjects();
MyJavaClass anObj = new MyJavaClass();
anObj。aValue = 2;
app。changeObject(anObj);
System。out。println(〃Java: 〃 + anObj。aValue);
}
private