转载自:http://www.ibm.com/developerworks/cn/java/j-jdic/
用 JDIC 在桌面应用程序中集成本机操作系统特性
简介: 本文将介绍 JDesktop Integration Components (JDIC),这是一个开源的项目,目的是构建消除本机应用程序和 Java 等价物之间差距的组件。JDIC 单一的 Java API 允许应用程序接进本机操作系统特性,同时保持跨平台支持。它目前提供了本机 Web 浏览器(Internet Explorer 或 Mozilla) 支持、系统托盘支持、文件扩展集成和其他桌面特性。
开发人员和用户对 Swing 最大的一个抱怨(也是对 Java 编程语言整体上的抱怨)是,客户端应用程序给人的感觉与本机应用程序的不同。(本机应用程序是指用采用操作系统内置库的编程语言编写的应用程序,例如运行在 Windows 上的 Visual C++ 应用程序。)虽然 Java 应用程序表现得像本机应用程序那样好,但是还是遗漏了某些地方,从而使用户注意到了差异。基本的问题是 Java 应用程序 不能真正地表现得像本机应用程序那样,因为本机应用程序有其不公平的一面:它们可以专门针对操作系统进行编写,这让它们可以利用特别的本机操作系统函数。Java 代码受到自己“随处运行”的咒语限制,并且调用本机函数(例如通过 Java 本机接口)是一个大忌。
消除差距
从开始的时候起,Java 语言就固守“编写一次,随处运行”的咒语。不幸的是,这个咒语对程序员已经变成“根本不能操作特定于操作系统的任何东西”。客户端应用程序的在这方面表现得最明显。比起本机应用程序(可以利用运行它们的操作系统的每个独特特性),用户第一眼就可以看到 Java 应用程序外观上与生俱来的不足。JDIC 项目包含多个组件,目的是提供新的跨平台解决方案,消除 Java 应用程序和应用程序的差距。JDIC 试图通过提供标准的 API(针对每个操作系统的 JNI 调用进行分支)弥补 Java 的每个不足之处。JDIC 还提供了本机库文件(.dll 或 .mo) ,可以用它们把 Java 调用连接到本机操作系统的内置功能。结果是产生一套单一的 Java API,可以根据它运行所在的操作系统,调用不同的本机代码。
直到现在,Java 开发人员一直希望用这个不足换取能够编写在任何操作系统上均可运行的软件的优势。但是,随着 Java SE 5 的发布,特别是 Sun 对增强 Java 桌面的推动,有些开发人员想让 Java 应用程序和本机应用程序之间的差异最终消失。创建 JDIC 就是了为消除这个差异(请参阅 参考资料)。就像 JDIC Web 站点声称的那样,JDIC 的目标是“让基于 Java 技术的(‘ Java 应用程序’) 在不牺牲平台独立性的情况下,成为当前桌面平台的一等公民。”
这本文将介绍 JDIC 项目,以及它如何成功地让 Java 客户端应用程序表现得更加本机化。通过一系列示例,您可以看到 JDIC 中目前所拥有的组件,以及尚在“孵化器”中(工作进行中)的一些组件。我们将要介绍 JDIC 所提供的差不多每个组件,在理想情况下,这些将激发您足够的兴趣,让您将 JDIC 应用到您的应用程序中,甚至有助于说服您也为这个项目做点贡献。
关于 JDIC 的一个重要说明是:它仍然是一个 beta 项目,这意味着它还没有得到任何 JDK 发行版的支持,未来任何时候都可能发生变化。所以一定要把必要的文件与基于 JDIC 的应用程序一同提供,因为没有 Java 虚拟机(JVM)会支持它。而且因为 API 有可能在发行版之间变化,所以这篇文章中的函数名称今后可能会有所不同。
它是一个包装
JDIC 项目有两个目标:维护 Java API 的跨平台,让程序可以在任何机器上运行,并支持人们使用的每个操作系统。因为新功能要求调用每个操作系统的本机函数,所以这显然处在一个很艰难的位置上。JDIC 是通过将其 Java API 作为所有这些本机调用的包装器来实现这一点的。JDIC 组件代码本身指出正在哪个操作系统上运行应用程序,并调用适当的 JNI 函数。(您可以看到,在代码内部有针对每个操作系统的 if/else 链。这就是跨平台兼容性的代价。)图 1 演示了 JDIC 包装本机调用的方式:
回页首
设置
编写和运行使用 JDIC 应用程序的机器设置比通常情况略微麻烦一些。除了通常的类路径要求之外,还必须提供 Java 类要调用的操作系统库。每个操作系统都有不同的操作系统库,所以如果想使用 JDIC 将应用程序移植到许多不同的操作系统上,则必须为每个操作系统提供一个库。使用 JDIC 发行版 0.9 时(也就是这篇文章所根据的版本),JDIC 可以处理 Windows、Linux 和 Solaris 系统(要对 Macintosh 的拥护者说抱歉了)。
开始下载跨平台 JDIC 包,其中包括:
这些文件设置好之后就可以启动并运行了。如果不能启动和运行,那么请参阅 JDIC 下载的 README 文件中的文档,查看特定操作系统的要求。一旦设置就绪,并做好了准备,请继续阅读以初步了解这些示例。
回页首
弥补差距
对于每个 JDIC 组件,我都将演示一个小的示例应用程序,每个组件都解决 Java 语言中目前存在的一个特定差距。
WebBrowser组件
JDIC 项目中的第一个组件(也是该项目的最初灵感) 是 WebBrowser,它提供了一条接近操作系统内置浏览器的途径。任何熟悉 Swing 的人都非常清楚,在 Java 应用程序中查看 Web 页面很困难。例如,现有的 Swing 组件 JEditorPane就做得很糟糕,只能显示最简单的 Web 页面。如果包含任何 DHTML 脚本,那么请忘记它 —— 现有的 Swing 组件不能显示它。这让 Swing 开发人员失去了在应用程序中显示 HTML 页面的选择 —— 由于 Web 在任何桌面应用程序中都是如此之重要(用作帮助页面和 README 文件),所以这是一个很大的障碍。
特别打击 Swing 开发人员的一个事实是:现在每台机器上都有功能齐全的浏览器,但它们的应用程序仍然无法使用。每个本机应用程序都能使用内置的浏览器(Windows 上的 Internet Explorer 或 Linux 机器上的 Mozilla)。Swing 的功能与易于访问的解决方案之间的巨大差距促使 WebBrowser成为 JDIC 项目的一个好起点。
WebBrowser允许 Swing 应用程序将本机浏览器嵌入任何应用程序中。但是需要重点指出的是,WebBrowser 仅仅是 Web 浏览器应用程序的呈现部分。这个组件不包含 Back按钮、地址栏、状态栏,或者任何不属于应用程序呈现部分的内容。但是,正如稍后将看到的,WebBrowser包含了允许您实现这些常用的浏览器特性的方法。
WebBrowser 组件继承自 java.awt.Canvas class,这意味着它是一个 Abstract Windowing Toolkit (AWT) 组件。这可能让熟悉 AWT 和 Swing 组件混合问题(包括重绘问题)的 Swing 开发人员开始担心。WebBrowser组件覆盖了java.awt.Canvas.paint()方法,直接把 HTML 呈现给 Canvas 本身,确保了性能的优化。但这对 Swing 开发人员意味着什么呢?这意味着必须把 WebBrowser包含在 JPanel中,才能在 Swing 中使用它。这并不像听起来那么坏。JPanel是包含WebBrowser的逻辑组件,其他 Swing 组件已经有内置的 HTML 支持了。
但是,在可以看到可视组件实际工作的同时,为什么还要一直谈论它们呢?以下将要运行的示例是随 JDIC 下载一起提供的,它与本文其余示例重新打包在一起,可以在一个下载中得到它们(请参阅 下载)。要查看 WebBrowser示例的实际工作,请运行demo.Browser.Browser主类。图 2 显示了 WebBrowser 示例的实际工作:
现在应该已经看到了 WebBrowser的实际工作,让我们再来看一些 WebBrowser类中的方法,它们可以让您创建完整功能的浏览器:
executeScript()方法允许程序执行 JavaScript 脚本,并在装入的 Web 页面上或 HTML 内容上执行脚本。这个函数很酷,因为可以用它改变 Web 站点的外观,甚至是改变第三方站点的外观:请试着把下面一行添加到演示应用程序中,放在页面输入的后面:
webBrowser.executeScript("document.bgColor='blue';"); |
这为每个装入的 Web 站点提供了蓝色背景,并覆盖了站点的默认背景色。
WebBrowserEvent/WebBrowserListener框架工作的方式与 Swing 中的每个事件 / 侦听框架是一样的,但在该例中,涉及WebBrowser能够触发的所有与 WebBrowser有关的操作。对 WebBrowser触发的事件感兴趣的类,应当通过 WebBrowser的addWebBrowserListener()方法,把自己添加成它的侦听器。
WebBrowserListener接口中必须实现的方法是:
简而言之,这就是 WebBrowser组件。WebBrowser类和 WebBrowser事件 / 侦听器框架中的所有方法都允许您创建完整的、实用的 Web 浏览器应用程序。它可能无法与 Internet Explorer 或 Firefox 相比,但是 Swing 开发人员从此不再受到该库对 Web 页面和 HTML 可怜支持的制约,可以在任何应用程序中放心地依赖于完整 Web 支持 —— 即使显示最困难的 Web 站点也可以。作为 JDIC 项目的灵感来源,WebBrowser在正确的方向上开始发挥其作用。
SystemTray组件
SystemTray组件是最近才添加到 JDIC 中的。它允许 Java 应用程序把图标放系统托盘上(在 Windows 中,是屏幕右下角包含音量图标这类图标的区域)。Windows 应用程序中一个不断增长的趋势就是把功能添加到系统托盘图标上。即时通信应用程序就是一个很好的例子。它们常常只通过系统托盘图标向用户提供许多可以访问的选项(例如关闭应用程序)。迄今为止,Java 应用程序还不能融入到这个潮流中。
像 JDIC 中的多数项目一样,这个组件的 API 简单易用。实际上,整个 org.jdesktop.jdic.tray包只有两个类:SystemTray抽象了系统托盘本身;TrayIcon抽象了系统托盘中的单个图标。
请运行示例应用程序 —— demo.jdic.TrayDemoFrame类 —— 观察应用程序如何使用 JDIC 的系统托盘特性(请参阅 下载)。在运行应用程序时,应当在 Windows 的系统托盘上看到 yin-yang 图标,就像图 3 中左上角那样。(在 Linux 和 Solaris 上,它的外观当然会有所不同。即使在 Windows 上,也不完全与图 3 中的相同,因为不可能得到系统托盘的菜单快照,所以这是一个重新制作的图片。)
这个示例中有趣的部分不是 JFrame本向,而是它如何处理与系统托盘的交互。请看一下示例应用程序中设置这部分的代码。第一步是得到机器的系统托盘的实例:
SystemTray tray = SystemTray.getDefaultSystemTray(); |
接下来几步是创建托盘图标:
final TrayIcon trayIcon = new TrayIcon(getTrayIcon(), "Tray Demo", getPopupMenu()); trayIcon.setIconAutoSize(true); trayIcon.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setVisible(true); } }); |
TrayIcon类(很不幸)目前对它与鼠标单击的交互方式进行了硬编码。(JDIC 用户正在呼吁修改这一点。) TrayIcon 在鼠标左键单击时将触发 ActionEvent,而在鼠标右击时则显示弹出菜单。
最后,向系统托盘添加新的 TrayIcon:
tray.addTrayIcon(trayIcon); |
这就可以了 —— 已经创建了一个漂亮的系统托盘图标,可以给 Java 应用程序提供一种非常本机的感觉了。
JDIC 项目最近向 TrayIcon类添加了一个 displayMessage()函数。它允许 TrayIcon显示 Windows 用户熟悉的“泡泡消息”。单击示例应用程序的 Alert按钮,就可以看到实际运用中的泡泡消息。以下是实现它的代码:
getBtnAlert().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { trayIcon.displayMessage("Alert", "This is an Alert Message", TrayIcon.INFO_MESSAGE_TYPE); } }); |
本机文件支持
目前,Java 应用程序无法使用打开文件的默认应用程序。例如,Java 应用程序不能在 Microsoft Word 中打开 .doc。它不知道每台机器上 Word 的路径,所以,也就无法知道 Microsoft Word 是不是处理 .doc 文件的首选应用程序。而且也没有可以从 Java 应用程序打印 .doc 文件的简便方法。
JDIC 的 Desktop类创建了一些简单的静态方法,允许 Java 应用程序与系统应用程序交互,从而解决了这些问题。系统应用程序构建到操作系统之中 —— 只有操作系统才知道哪个应用程序应该打开 .doc 文件。
这个组件不像 SystemTray组件那么有用,但是确实弥补了一些空白。如果要处理不同的文件类型,例如文件浏览器,那么任何 Java 应用程序都可以使用 Desktop类的方法:
可以在示例程序 demo.jdic.FileExtensionDemo中看到这些函数的实际作用。应用程序顶部允许浏览查找文件,然后打开文件,如图 4 所示:
在单击 Open按钮时,Desktop类将调用系统默认的 PDF 阅读器,就像在下面的代码中可以看到的那样:
else if (e.getSource() == getOpenButton()) { try { Desktop.open(new File(getTxtFile().getText())); } catch (DesktopException ex) { ex.printStackTrace(); } } |
FileTypes组件
目前 JDIC 项目的最后一部分是 FileTypes。这个组件第一眼看起来可能不太重要,但是在我描述它之后,您就会看出它是创建整体 Java 桌面应用程序的非常重要的一个组成部分。FileType解决的基本问题是:现有 Java 程序不能登记文件扩展名并把它们与应用程序关联。在双击扩展名为 .doc 的文件时,Windows 会自然而然地知道要在 Word 中打开这个文件。Java 应用程序无法做到这点。无法编写这样的 Java 应用程序,让它在双击具有新扩展名的文件时自动打开。也不能登记像“jexx”这样的扩展名打开自己的 Java 应用程序。这对 Java 应用程序开发人员来说是一个严重的倒退,因为这让会他们的应用程序感觉起来不像本机操作系统的一部分 —— 在用户双击文件时,是希望他们不得不导航到 Open with... 对话框,还是希望他们打开应用程序呢?
org.jdesktop.jdic.filetypes包中的三个类弥补了这个差距,它们允许 Java 应用程序登记文件扩展名,并把它们与 open、edit 和 print 动作关联。可以选择让不同的应用程序进行打开和编辑。例如,应用程序可以用浏览器打开 XML 文件,并用文本编辑器编辑它。把动作连接到关联之后,就可以把它登记到操作系统上。
现在开始研究 demo.jdic.FileExtensionDemo类 (与前一节讨论的 本机文件支持是同一个应用程序)。 这次,将使用对话框的下半部分创建自己的 FileType。图 5 显示了在填充了字段之后该示例看起来的样子:
在单击 Register按钮之后,可以在 Windows 文件夹选项对话框中打开 File Types 选项卡,查看刚才创建的内容,如图 6 所示:
现在看一看示例应用程序中相关部分的代码:
else if (e.getSource() == getRegisterButton()) { String verb = getTxtVerb().getText(); String command = getTxtCommand().getText(); String fileExt = getTxtExt().getText(); // Create the Action object that will store the // verb (in this example "open" with the command "in this case the // link to the java.exe Action action = new Action(verb, command); // Next we need to create the Association which we'll use to // register in the operating system // You can link the action we just created to the // association and a corresponding file extension. Association association = new Association(); association.addAction(action); association.addFileExtension(fileExt); // Finally, after we have created our Association, we use the // AssociationService, which will talk directly to the operating system to // register the file type AssociationService associationService = new AssociationService(); try { associationService.registerSystemAssociation(association); } catch (RegisterFailedException ex) { ex.printStackTrace(); } catch (AssociationAlreadyRegisteredException ex) { ex.printStackTrace(); } } |
这个示例介绍了在操作系统中创建和登记文件类型有多简单,Association类中一些有趣的附加方法可以增强文件类型:
作为最后一步,为了显示本机文件支持和 FileTypes一起发挥作用的威力,请在桌面上创建叫做 example.jexx 的文件,如图 7 所示:
现在,不需双击 example.jexx 文件(它什么也证明不了),请使用在 本机文件支持中刚刚学到的 JDIC 代码,通过FileExtensionDemo应用程序打开它。
在单击 Open按钮时,会看到这个文件启动了 Notepad。本机文件支持和 FileType组件的结合是 Java 应用程序开发人员工具箱中的一个重要新增功能。对 Java 应用程序的可执行 JAR 文件实现本机支持双击的日子到来了。现在用户终于可以通过双击应用程序关联的文件来打开 Java 应用程序了。这一个小小的但是独特的新增特性让 Java 应用程序看起来就像是操作系统天生的一部分一样。
回页首
孵化器中
迄今为止我描述的组件都已经很完善,并成为 JDIC 项目的有用部分。JDIC 还包含一组建设中的项目,叫做 孵化器项目(也叫做沙箱)。在孵化器项目中可以发现许多完成程度,从已经完成的组件一直到等候进入 JDIC 包的条目,以及仍然是纸上设想的组件。
作为公开项目(公开想做贡献的任何人的想法和努力成果)的一个组成部分,孵化器提供了一个促使新想法产生的测试机会。只有测试过而且 API 和功能得到批准的组件才会升级到最后发布的 JDIC 包。在阅读这一节的孵化器组件时,要记住的一点是 —— 它们很酷,但是不完整。距离它们准备好登场可能还有几个月的时间。
IconService
IconService类执行单一的任务:把图标名称(通常采用难以理解的形式,例如 C:\Program Files\IBM\eclipse.exe,1) 转换成java.awt.Image对象。一旦获得了 Image对象,就可以对其进行操作,所以可以操纵图标本身。图标名称是通过调用getIconFileName()方法从另一个 JDIC 类得到的,这个类就是 Association类。下面是把图标转换成 Image对象的方法:
Image i = IconService.getIcon(Association.getIconFileName(), 1); |
SystemInfo
SystemInfo类试图提供操作系统本身的信息。这包含诸如系统空闲时间、用户会话是否锁定之类的项目。在编写这篇文章时,它提供的内容只有这些,没有用来浏览或阅读这些方法的 API。这个类好像仍然还处在婴儿期,所以请耐心等待,看看是否会有更新。
FloatingDock
FloatingDock类的目标是模拟 Windows 的任务栏,它既允许在上面添加组件,也允许把自己停靠在屏幕的某个区域上。FloatingDock基本上是个绑定到某个区域(左、右、下、上)的 JPanel。它包含的功能大部分与 JPanel相同,并且允许改变布局,添加和删除组件。FloatingDock还允许应用程序在任何时候改变区域,非常像把 Windows 的任务栏拖到屏幕上的新位置。
不要被误导,以为 FloatingDock提供了与任务栏一样的功能。它只是支持停靠到某个屏幕区域。如果想模拟任务栏、添加显示JToggleButton的功能、改变它们的大小等,那么这些将完全取决于您自己。
下面展示了如何在屏幕底部创建 FloatingDock对象并向它添加 JButton:
FloatingDock dock = new FloatingDock(); dock.setLocation(FloatingDock.BOTTOM); dock.add(new JButton("Window 1")); |
Misc包
Misc孵化器包包含一个挑选出来的单任务组件集合。这些组件只包含一个或两个静态方法,试图弥补 Java 到本机转换的某个简单差距。在编写这篇文章时,这个包中的类只支持 Macintosh,因为开发人员感到在这个项目中 Mac 没有得到应得的重视。目前,还没有部署任何东西,因为项目还没有为 Mac 完成一个构建。所以目前来说,最多只能阅读它们的方法和它们能做什么,然后,期望在不远的将来会发布一个发行版。
Volume
Misc包的成员之一 Volume类控制着计算机的音量。public floatVolume.getInstance().getVolume()得到当前音量,public voidVolume.getInstance().setVolume(float vol)设置音量。
这个类还允许用户记录音量上的变化,它提供了一个 addPropertyChangeListener()函数,在计算机的音量发生变化时,该函数会通知对此感兴趣的类 (但是不一定要通过 Volume类本身)。
Wallpaper
Misc包的另一个成员 Wallpaper类及其对应的 WallpaperFactory类允许 Java 应用程序改变操作系统的墙纸(桌面上显示在背景中的图片)。以下是改变墙纸的一些示例代码:
Wallpaper wall = WallpaperFactory.createWallpaper(); wall.setBackground(new File("/background.jpg"), Wallpaper.CENTER); |
Misc包的另一个成员 Alerter类添补了一个很小的空白,它让不在焦点的应用程序(例如,在其他窗口后面)通知用户需要执行某些动作。在 Macs 上(也是目前支持这个类的地方),这个 Alert 会“弹回”停靠图标。在 Windows 上,在得到支持的时候,会闪动任务栏图标,直到该图标被选中。以下是一个代码示例:
Alerter.newInstance().alert(); |
Dock Menu
Misc包中的最后一个类解决了停靠菜单(Dock Menu),这个组件只在 Macs 中存在,在 Windows 或 Linux 系统中没有对应的组件。DockMenu类允许用户把菜单附着到 Mac 应用程序的 Dock Menu 中。以下是如何使用 DockMenu类的一个示例:
JMenu menu = new JMenu("Exit"); DockMenu.newInstance().setMenu(menu); |
FileUtil
JDIC 孵化器的最新添加是 FileUtil,这个类提供了一些新功能,补充了 JDK 中的 java.io.File类。public java.math.BigIntegergetFreeSpace(File dir)方法返回目录中的剩余空间,public boolean recycle(File f)把文件放在操作系统的回收站,而不是删除它。
回页首
期望列表
JDIC 和孵化器中的组件列表相当详尽,但是决不意味着弥补了 Java 应用程序和本机应用程序之间的所有差距。这一节是我和其他许多开发人员都想看到的 JDIC 中已实现的组件的期望列表。对于那些有想法的读者来说,需要记住的是,JDIC 是一个开源项目,任何人在任何时候都可以对其做贡献 —— 所以请放心地把这些想法付诸于行动吧。
本机支持的非矩形窗口
非矩形帧在应用程序中变得非常流行(例如,Microsoft 媒体播放器)。对跨多操作系统非矩形窗口的支持,Swing 开发人员对此已经呼吁了好多年。如果开发人员在 JDIC 中添加了对它的支持,那么整个 Swing 社区就会立即把它们捧上天。(现有解决方案中的方法包括来自 L2FProd.com 项目的只能用于 Windowsr 解决方案和我自己的 IFrame,这是一个“接近完成”的解决方案,请参阅 参考资料。)
桌面的“New”支持
当用户在 Winows 桌面上右击时,他们会看到 New菜单选项,里面包含的子菜单中有用户可以创建的常见文件类型。目前 Java 语言还不能访问这个菜单。
特定文件的右击支持
用户在 Winows 中的文件上右击时,会呈现对这个文件的选择。有些应用程序会随时自动地改变它们在这个选择列表中的默认行为。(例如,请注意 WinZip,如果已经在机器上安装该软件的话,那么在右击任何文件类型时,它会始终是其中的一个选择。Java 语言无法永久地向这个菜单添加程序或选择。
System类中的附加功能
SystemInfo类中的附加功能可能很有用,其中包括支持查找进程名称和编号、网络状态、网络连接速度和电池使用寿命。
用户和组管理
如果 Java 应用程序能够管理操作系统的用户和访问组,包括处理口令管理,那会很棒。
时间和日期
如果让 Java 程序可以改变操作系统的时间、日历和时区,那会怎么样?
结束语
一般来说,JDIC 项目弥补了在 Swing 和 Java 程序语言之间的主要差距。随着其他方面的不断努力,差距会进一步缩小,总有一天,用户会无法区分 Java 应用程序和本机应用程序。
已经看到,JDIC (在编写这篇文章时)既包含成熟的、为生产作好准备的项目,又包含孵化器项目(包含不同开发阶段的项目)。孵化器提供了一些有趣的项目,不久的将来,这些项目可能会走向成熟。重要的是要记住:如果决定冒险在应用程序中包含孵化器项目,由于孵化器随时可能会改变,可能会带来一些问题,甚至可能使应用程序根本不工作。
我的建议是,如果喜欢在本文中看到的内容,那就最好加入 JDIC 项目。这个项目对所有有兴趣参与的人都是开放的,而且就像多数开源项目一样,它能做到的取决于人们想要贡献的(请参阅 参考资料,以获得登录您打算参与的项目所在站点的直接链接)。不管是想要帮助完成孵化器中的某个项目,还是决定跟踪我的期望列表中的某个想法,或是想看到自己的想法走向成熟,开发社区都会极大地感谢您的帮助。不要认为自己必须是一名本机代码或 JNI 专家,或者必须知道每个操作系统。孵化器项目会让您推敲您的想法,并让您从其他志愿者那里得到反馈和帮助。
回页首
下载
描述 |
名字 |
大小 |
下载方法 |
Sample code |
j-jdic.jar |
12 KB |
HTTP |
关于下载方法的信息
参考资料