返回介绍

A.1.1 调用固有方法

发布于 2024-10-15 23:56:37 字数 3113 浏览 0 评论 0 收藏 0

我们先从一个简单的例子开始:一个 Java 程序调用固有方法,后者再调用 Win32 的 API 函数 MessageBox(),显示出一个图形化的文本框。这个例子稍后也会与 J/Direct 一志使用。若您的平台不是 Win32,只需将包含了下述内容的 C 头:

#include <windows.h>

替换成:

#include <stdio.h>

并将对 MessageBox() 的调用换成调用 printf() 即可。

第一步是写出对固有方法及它的自变量进行声明的 Java 代码:

class ShowMsgBox {
  public static void main(String [] args) {
    ShowMsgBox app = new ShowMsgBox();
    app.ShowMessage("Generated with JNI");
  }
  private native void ShowMessage(String msg);
  static {
    System.loadLibrary("MsgImpl");
  }
}

在固有方法声明的后面,跟随有一个 static 代码块,它会调用 System.loadLibrary()(可在任何时候调用它,但这样做更恰当)System.loadLibrary() 将一个 DLL 载入内存,并建立同它的链接。DLL 必须位于您的系统路径,或者在包含了 Java 类文件的目录中。根据具体的平台,JVM 会自动添加适当的文件扩展名。

1. C 头文件生成器:javah

现在编译您的 Java 源文件,并对编译出来的.class 文件运行 javah。javah 是在 1.0 版里提供的,但由于我们要使用 Java 1.1 JNI,所以必须指定-jni 参数:

javah -jni ShowMsgBox

javah 会读入类文件,并为每个固有方法声明在 C 或 C++头文件里生成一个函数原型。下面是输出结果——ShowMsgBox.h 源文件(为符合本书的要求,稍微进行了一下修改):

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

#ifndef _Included_ShowMsgBox
#define _Included_ShowMsgBox
#ifdef __cplusplus
extern "C" {
#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 文档。

3. 实现自己的 DLL

此时,我们要做的全部事情就是写一个 C 或 C++源文件,在其中包含由 javah 生成的头文件;并实现固有方法;然后编译它,生成一个动态链接库。这一部分的工作是与平台有关的,所以我假定读者已经知道如何创建一个 DLL。通过调用一个 Win32 API,下面的代码实现了固有方法。随后,它会编译和链接到一个名为 MsgImpl.dll 的文件里:

#include <windows.h>
#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,同时对上述代码进行更详尽的解释。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文