用Java代码处理本地对象的事件
来源:网络 更新时间:2014-12-2
当您需要使用以其他语言编写的对象时,本地事件源和Java?侦听器之间的通信可能需要一些小技巧——尤其是在多线程环境中。本文通过使用一种透明处理从本地代码到JVM的事件通信的设计模式,帮助您有效地处理传统的本地库。
在面向对象系统中,对象可以触发一组事件。Java编程语言为定义基于观察者设计模式(Observerdesignpattern)的事件侦听器提供了支持,但当您需要使用以其他语言编写的对象时,这还不够。使用JavaNativeInterface(JNI)在本地事件源和Java侦听器之间进行通信,可能需要一些技巧,尤其是有多线程环境中。在本文中,我们描述了一种透明处理从本地代码到JVM的事件通信的设计模式。您可以使用这种设计来提供到遗留本地应用程序的Java接口,或者构建带Java侦听器的本地应用程序。
观察者设计模式
观察者设计模式定义了事件侦听器与事件创建者之间的多对一依赖关系。当事件创建者触发一个事件时,其所有侦听器接收到该事件的通知。由于事件创建者和侦听器是无关的,您可以单独使用或修改它们。这种设计模式是事件驱动编程的核心,被广泛用于GUI框架,比如Swing和SWT。
如果整个应用程序都使用Java编程语言编写,实现观察者设计模式相当简单。图1中的类图给出了一个例子:
图1.观察者设计模式
Java本地应用程序侦听器
不过,在某些情况下,我们想要让本地应用程序支持Java侦听器。大量应用程序(包括应用程序入口点)可能以本地代码编写,而应用程序以与其用户界面交互的方式生成事件。在这种情形下,支持基于Java用户界面的最佳方式是让Java类将自身注册为应用程序生成的各种事件的侦听器。简而言之,通过支持以Java语言编写的侦听器,可以获得支持Java的本地应用程序。
图2所示的类图给出了一个示例场景。CEventSource类用C 语言编写。它使用addMouseDownListener()和removeMouseDownListener()让侦听器注册和取消注册其“鼠标按下”事件。它想要所有侦听器实现IMouseDownListener接口。
图2.示例场景的类图
注意,IMouseDownListener是一个C 抽象类。那么,Java类如何注册事件才不会引入Java侦听器和CEventSource类之间的编译时绑定呢?在其注册事件后,CEventSource类如何调用Java类中的方法呢?这正是JavaInvocationAPI的用武之地。
JavaInvocationAPI
InvocationAPI让您可以将JVM加载到一个本地应用程序中,而不必显式地链接JVM源。通过在jvm.dll中调用一个函数,可以创建一个JVM,jvm.dll还将当前本地线程连接到JVM。然后,您可以在JVM中从本地线程调用所有Java方法。
然而,InvocationAPI无法彻底解决问题。您不希望CEventSource类具有与Java侦听器的编译时依赖关系。另外,Java侦听器不应该承担使用JNI来注册带CEventSource的侦听器的责任。
代理设计模式
通过使用代理设计模式(Proxydesignpattern),可以避免这一弊端。通常来说,代理是另一个对象的占位符。客户端对象可以处理代理对象,而代理封装了所有使用本地方法的细节。代理模式的细节显示在图3中:
图3.代理设计模式示例
EventSourceProxy是CEventSource类的代理。要将其自身注册为一个侦听器,客户端应该实现IJMouseDownListener接口。该接口类似于IMouseDownListener,但它是用Java代码编写的。当第一个客户端调用其addMouseDownListener()方法时,它使用reGISterListener()本地方法来将其自身注册为一个带CEventSource的侦听器。registerListener()方法用C 语言来实现。它创建一个JMouseDownListener对象,并将其注册为一个带CEventSource的侦听器。当JMouseDownListener的onMouseDown事件被触发时,JMouseDownListener中的onMouseDownListener()方法使用InvocationAPI来通知EventSourceProxy。