在默認(rèn)GUI外觀、打印和運(yùn)行性能方面,Java平臺(tái)一直在努力縮小本機(jī)應(yīng)用程序和Java應(yīng)用程序程序是之間的差距。隨著Java SE 6(代碼名為Mustang)的問(wèn)世,一些新的功能又被加入,包括新的系統(tǒng)托盤(pán)功能,更好的打印支持和桌面API(java.awt.Desktop API),從而進(jìn)一步縮小以上差距。本文中描述的這些新型桌面API允許Java應(yīng)用程序與主機(jī)平臺(tái)上的特定文件類型的默認(rèn)應(yīng)用程序進(jìn)行交互。為了更有效地描述這些API,本文還將向你展示一個(gè)簡(jiǎn)單的示例應(yīng)用程序DesktopDemo。

  一、 桌面概述

  這種新功能是由java.awt.Desktop類所提供的。這種API來(lái)源于JDesktop集成組件(JDIC)工程。該工程的目的是,使得基于Java技術(shù)的應(yīng)用程序成為桌面平臺(tái)上的"第一等公民",并實(shí)現(xiàn)與桌面API的無(wú)縫集成。具體地說(shuō),這種新型桌面API允許你的Java應(yīng)用程序?qū)崿F(xiàn)如下功能:

  · 使用一個(gè)特定的統(tǒng)一資源標(biāo)志符(URI)啟動(dòng)主機(jī)系統(tǒng)的默認(rèn)瀏覽器

  · 啟動(dòng)主機(jī)系統(tǒng)的默認(rèn)電子郵件客戶端

  · 啟動(dòng)特定的應(yīng)用程序以打開(kāi)、編輯或打印與之相關(guān)聯(lián)的文件

  這些桌面API使用你的主機(jī)操作系統(tǒng)的文件關(guān)聯(lián)以啟動(dòng)與特定文件類型相關(guān)聯(lián)的應(yīng)用程序。例如,如果開(kāi)放文檔文本(.odt)文件擴(kuò)展名與OpenOffice書(shū)寫(xiě)器應(yīng)用程序相關(guān)聯(lián),那么你的Java應(yīng)用程序就可以啟動(dòng)OpenOffice書(shū)寫(xiě)器以打開(kāi)、編輯或打印與這種關(guān)聯(lián)相關(guān)的文件。根據(jù)你的主機(jī)系統(tǒng)的不同,不同的應(yīng)用程序可能關(guān)聯(lián)不同的行為。

  二、 運(yùn)行DesktopDemo應(yīng)用程序

  DesktopDemo是一個(gè)簡(jiǎn)單Java應(yīng)用程序-它使用了Mustang的桌面API。該應(yīng)用程序提供了一個(gè)主窗口,允許你實(shí)現(xiàn)如下三項(xiàng)功能:

  1. 以一個(gè)特定的URI啟動(dòng)默認(rèn)瀏覽器。

  2. 用一個(gè)郵件接收者啟動(dòng)默認(rèn)電子郵件客戶端。

  3. 啟動(dòng)一個(gè)相關(guān)聯(lián)的應(yīng)用程序以打開(kāi)、編輯或打印文件。

  圖1顯示了這個(gè)用戶接口(UI)。


圖1:DesktopDemo用戶接口

  你可以通過(guò)下載應(yīng)用程序源代碼及相關(guān)的JAR文件來(lái)運(yùn)行這個(gè)應(yīng)用程序-把你的控制臺(tái)的活動(dòng)目錄改變?yōu)樵搼?yīng)用程序工程的dist目錄,并且使用一個(gè)Mustang JDK執(zhí)行下列命令:

java -jar DesktopDemo.jar

  三、 確定是否支持Desktop API

  在啟動(dòng)瀏覽器、電子郵件客戶端或任何應(yīng)用程序之前,DesktopDemo必須確定是否你的平臺(tái)支持這種API。然而,DesktopDemo首先停用所有的圖形化的文本域和按鈕。在確定該平臺(tái)支持它們之后它該程序才啟用這些圖形組件。

  在實(shí)例化這些UI后,該應(yīng)用程序的構(gòu)造器快速停用這個(gè)應(yīng)用程序的少數(shù)幾個(gè)組件,如下列代碼所示:

public DesktopDemo() {
 //初始化所有的GUI組件.
 initComponents();
 // 停用啟動(dòng)瀏覽器和電子郵件客戶端的按鈕
 // 停用打開(kāi),編輯和打印文件的按鈕
 disableActions();
 ...
}
/**
* 停用所有的圖形組件,直到我們了解
* 是否支持它們的功能.
*/
private void disableActions() {
 txtBrowserURI.setEnabled(false);
 btnLaunchBrowser.setEnabled(false);

 txtMailTo.setEnabled(false);
 btnLaunchEmail.setEnabled(false);
 rbEdit.setEnabled(false);
 rbOpen.setEnabled(false);
 rbPrint.setEnabled(false);
 txtFile.setEnabled(false);
 btnLaunchApplication.setEnabled(false);
}
...
public javax.swing.JTextField txtBrowserURI;
public javax.swing.JButton btnLaunchBrowser;
public javax.swing.JTextField txtMailTo;
public javax.swing.JButton btnLaunchEmail;
public javax.swing.JRadioButton rbEdit;
public javax.swing.JRadioButton rbOpen;
public javax.swing.JRadioButton rbPrint;
public javax.swing.JTextField txtFile;
public javax.swing.JButton btnLaunchApplication;

  使用Desktop.isDesktopSupported()方法來(lái)確定是否桌面API可用。在Solaris操作系統(tǒng)和Linux平臺(tái)上,這種API是依賴于Gnome庫(kù)的。如果這些庫(kù)不可用,那么這個(gè)方法將返回false。在確定支持這種API(也就是說(shuō),isDesktopSupported()返回true)之后,該應(yīng)用程序就可以使用靜態(tài)方法getDesktop()來(lái)檢索一個(gè)Desktop實(shí)例。

Desktop desktop = null;
//在使用更多的Desktop API前,首先檢查
//是否這種API為該特定主機(jī)上的特別的虛擬機(jī)所支持。
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
...

  如果你的應(yīng)用程序在調(diào)用getDesktop()之前不使用isDesktopSupported()進(jìn)行API支持檢查,那么它必須準(zhǔn)備捕獲一個(gè)UnsupportedOperationException異常-當(dāng)你的應(yīng)用程序在一個(gè)不支持這種特性的平臺(tái)上請(qǐng)求一個(gè)Desktop實(shí)例時(shí)將拋出這種異常。另外,如果你的應(yīng)用程序運(yùn)行于一種無(wú)鍵盤(pán)、鼠標(biāo)和監(jiān)視器環(huán)境下,該getDesktop()方法將拋出一個(gè)java.awt.HeadlessException異常。

  一旦檢索完畢,該Desktop實(shí)例即允許你的應(yīng)用程序?yàn)g覽、郵寄、打開(kāi)、編輯或甚至打印一個(gè)文件或URI,但是只有在被檢索的Desktop實(shí)例支持這些活動(dòng)的前提下才行。每個(gè)這些活動(dòng)被稱為一個(gè)行為(Action),并且每一個(gè)行為被描述為一個(gè)Desktop.Action枚舉實(shí)例:

  · BROWSE-描述主機(jī)的默認(rèn)瀏覽器執(zhí)行的一種瀏覽行為

  · MAIL-描述主機(jī)的默認(rèn)電子郵件客戶端執(zhí)行的一種郵件行為

  · OPEN-描述一種與打開(kāi)一特定的文件類型相關(guān)聯(lián)的應(yīng)用程序執(zhí)行的打開(kāi)行為

  · EDIT-描述一種與編輯一特定的文件類型相關(guān)聯(lián)的應(yīng)用程序執(zhí)行的編輯行為

  · PRINT-描述一種與打印一特定的文件類型相關(guān)聯(lián)的應(yīng)用程序執(zhí)行的打印行為


在調(diào)用任何這些行為之前,一個(gè)應(yīng)用程序必須確定是否該Desktop實(shí)例支持它們。這與確定是否一個(gè)Desktop實(shí)例可用是有所不同的。這個(gè)Desktop.isDesktopSupported()方法告訴你是否能夠創(chuàng)建一個(gè)實(shí)例。一旦獲得一個(gè)Desktop對(duì)象,你就可以查詢?cè)搶?duì)象來(lái)確定支持哪些特定類型的行為。如果該Desktop對(duì)象不支持特定的行為,或如果該桌面API本身并不被支持,那么DesktopDemo簡(jiǎn)單地停用那些受影響的圖形組件。如圖2所示,在停用狀態(tài)下,不能使用這些組件來(lái)調(diào)用桌面特性。


圖2:當(dāng)不支持桌面API時(shí)圖形組件被停用。

  通過(guò)使用一個(gè)新的Desktop實(shí)例,下列代碼檢查負(fù)責(zé)是否支持Desktop.Action并且啟用適當(dāng)?shù)膱D形組件:

public DesktopDemo() {
 ...
 //在使用更多的桌面API前,首先檢查
 //是否這種API為該特定主機(jī)上的特別的虛擬機(jī)所支持。
 if (Desktop.isDesktopSupported()) {
  desktop = Desktop.getDesktop();
  // 現(xiàn)在,啟用按鈕以實(shí)現(xiàn)被支持的行為
  enableSupportedActions();
 }
 ...
}
/**
*啟用在該主機(jī)上被支持的行為。
*這些行為有:打開(kāi)瀏覽器,
*打開(kāi)電子郵件客戶端,和使用它們相關(guān)聯(lián)的應(yīng)用程序打開(kāi),編輯與打印文件。
*/
private void enableSupportedActions() {
 if (desktop.isSupported(Desktop.Action.BROWSE)) {
  txtBrowserURI.setEnabled(true);
  btnLaunchBrowser.setEnabled(true);
 }

 if (desktop.isSupported(Desktop.Action.MAIL)) {
  txtMailTo.setEnabled(true);
  btnLaunchEmail.setEnabled(true);
 }
 if (desktop.isSupported(Desktop.Action.OPEN)) {
  rbOpen.setEnabled(true);
 }
 if (desktop.isSupported(Desktop.Action.EDIT)) {
  rbEdit.setEnabled(true);
 }
 if (desktop.isSupported(Desktop.Action.PRINT)) {
  rbPrint.setEnabled(true);
 }

 if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) {
  txtFile.setEnabled(true);
  btnLaunchApplication.setEnabled(true);
 }
}

  一旦該應(yīng)用程序確定了被支持的行為,它即啟用適當(dāng)?shù)膱D形組件。如果所有的組件都被啟用,那么相應(yīng)的UI應(yīng)該看上去如圖3所示。


圖3:當(dāng)支持桌面API時(shí),啟用組件。

  四、 打開(kāi)瀏覽器

  調(diào)用下列實(shí)例方法將打開(kāi)你的主機(jī)的默認(rèn)瀏覽器:

public void browse(URI uri) throws IOException

  因?yàn)閮H當(dāng)支持相關(guān)聯(lián)的Desktop.ActionDesktopDemo時(shí),UI組件才被啟用,所以,在實(shí)際調(diào)用browse()方法之前,這個(gè)簡(jiǎn)單的演示應(yīng)用程序不需要進(jìn)行行為支持檢查。然而,在每一種調(diào)用之前檢查行為支持在實(shí)際中將增加程序的健壯性:

if (desktop.isSupported(Desktop.Action.BROWSE)) {
 //啟動(dòng)瀏覽器
 ...
}

  DesktopDemo把一個(gè)java.awt.event.ActionListener添加到每一個(gè)按鈕上。當(dāng)被啟用時(shí),"Launch Browser"按鈕通過(guò)它的ActionListener調(diào)用下列方法:

private void onLaunchBrowser(java.awt.event.ActionEvent evt) {
 URI uri = null;
 try {
  uri = new URI(txtBrowserURI.getText());
  desktop.browse(uri);
 }
 catch(IOException ioe) {
  ioe.printStackTrace();
 }
 catch(URISyntaxException use) {
  use.printStackTrace();
 }
 ...
}

  這個(gè)browse()方法可能拋出各種類型的異常,這包括:當(dāng)該URI為null時(shí)拋出一個(gè)NullPointerException異常;如果不支持BROWSE行為將拋出一個(gè)UnsupportedOperationException異常;如果不能發(fā)現(xiàn)或啟動(dòng)一個(gè)缺省的瀏覽器或應(yīng)用程序則拋出一個(gè)IOException異常;如果一個(gè)安全管理器否定一次調(diào)用則拋出一個(gè)SecurityException異常。

  然而,如果一切順利,那么聽(tīng)取器(Listener)將從圖4中相聯(lián)系的文本域中檢索文本,創(chuàng)建一個(gè)URI并且調(diào)用browse()方法。上面的代碼將啟動(dòng)你的系統(tǒng)的默認(rèn)瀏覽器并且指示該瀏覽器裝載該URI,如圖5所示。


圖4:使用一個(gè)特定URI啟動(dòng)默認(rèn)瀏覽器。

圖5:使用桌面API啟動(dòng)默認(rèn)瀏覽器。



五、 發(fā)送電子郵件

  如果支持該行為的話,該應(yīng)用程序能夠啟動(dòng)主機(jī)的默認(rèn)電子郵件客戶端-通過(guò)調(diào)用這個(gè)Desktop實(shí)例方法:

public void mail(URI uri) throws IOException
DesktopDemo為"Launch Mail"按鈕提供了一個(gè)ActionListener。在這種情況中,該聽(tīng)取器調(diào)用下列方法:
private void onLaunchMail(java.awt.event.ActionEvent evt) {
 String mailTo = txtMailTo.getText();
 URI uriMailTo = null;
 try {
  if (mailTo.length() > 0) {
   uriMailTo = new URI("mailto", mailTo, null);
   desktop.mail(uriMailTo);
  } else {
   desktop.mail();
  }
 }
 catch(IOException ioe) {
  ioe.printStackTrace();
 }
 catch(URISyntaxException use) {
  use.printStackTrace();
 }
 ...
}

  該onLaunchMail()方法從相關(guān)的文本域中檢索電子郵件接收者,并且在存在一位接收者時(shí)使用一種mailto模式的參數(shù)創(chuàng)建URI,然后調(diào)用mail()方法。這個(gè)mail()方法被重載,這樣你可以使用(或不使用)一個(gè)描述其mailto接收者的URI(見(jiàn)圖6)來(lái)調(diào)用這個(gè)方法。


圖6:使用一個(gè)電子郵件接收者啟動(dòng)默認(rèn)電子郵件客戶端。

  當(dāng)創(chuàng)建這個(gè)URI時(shí),你可以使用多個(gè)電子郵件接收者。這個(gè)mailto模式支持CC,BCC,SUBJECT和BODY域。例如,可以使用下列文本來(lái)創(chuàng)建一個(gè)mailto URI:

mailto:duke@sun.com?SUBJECT=Happy New Year!&BODY=Happy New Year, Duke!

  圖7顯示出相應(yīng)的結(jié)果。


圖7:桌面API使用多個(gè)mailto參數(shù)啟動(dòng)默認(rèn)電子郵件客戶端。

  當(dāng)然,你也可以不使用參數(shù)來(lái)調(diào)用mail()。在這種情況中,你的電子郵件客戶端將啟動(dòng)一個(gè)新的沒(méi)有指定接收者、主題或郵件正文的電子郵件窗口。

  六、 打開(kāi)、編輯和打印文件

  Java應(yīng)用程序可以分別使用一個(gè)Desktop對(duì)象的open(),edit()和print()方法來(lái)從與其相聯(lián)系的應(yīng)用程序中打開(kāi),編輯和打印文件(見(jiàn)圖8)。同樣,僅在該Desktop實(shí)例支持它們時(shí),DesktopDemo才允許這些行為,因此在本應(yīng)用程序環(huán)境下,不必再次進(jìn)行這種支持檢查。


圖8:啟動(dòng)與一特定的文件類型相聯(lián)系的應(yīng)用程序。

  DesktopDemo中的每一個(gè)單選按鈕也都有它自己的ActionListener。在這種情況中,每一個(gè)單選按鈕都設(shè)置一個(gè)實(shí)例變量,以便描述最近選擇的按鈕的相關(guān)聯(lián)Desktop.Action:

Desktop.Action action;
private void onPrintAction(java.awt.event.ActionEvent evt) {
 action = Desktop.Action.PRINT;
}
private void onEditAction(java.awt.event.ActionEvent evt) {
 action = Desktop.Action.EDIT;
}
private void onOpenAction(java.awt.event.ActionEvent evt) {
 action = Desktop.Action.OPEN;
}

  當(dāng)你按下"Launch Default Application"按鈕時(shí),它調(diào)用它自己的聽(tīng)取器-這將調(diào)用下列方法:

private void onLaunchDefaultApplication(java.awt.event.ActionEvent evt) {
 String fileName = txtFile.getText();
 File file = new File(fileName);
 try {
  switch(action) {
   case OPEN:
    desktop.open(file);
    break;
   case EDIT:
    desktop.edit(file);
    break;
   case PRINT:
    desktop.print(file);
    break;
  }
 }
 catch (IOException ioe) {
  ioe.printStackTrace();
 }
 ...
}

  這個(gè)方法決定選擇哪個(gè)Desktop.Action并且調(diào)用適當(dāng)?shù)腄esktop實(shí)例方法-open(),edit()或print()。每個(gè)方法都需要一個(gè)File參數(shù)-它被用于執(zhí)行要求的行為。

  有趣的是,不同的應(yīng)用程序可以針對(duì)甚至相同的文件類型上的這些不同的行為進(jìn)行注冊(cè)。例如,可以使用OPEN行為啟動(dòng)Firefox瀏覽器,使用EDIT行為啟動(dòng)Emacs,甚至使用PRINT行為啟動(dòng)另外不同的應(yīng)用程序。你的主機(jī)桌面的關(guān)聯(lián)用來(lái)決定應(yīng)該調(diào)用什么樣的應(yīng)用程序。

  注意 使用Mustang中現(xiàn)有桌面API來(lái)操作桌面文件關(guān)聯(lián)是不可能的,而且目前只能使用平臺(tái)依賴的工具來(lái)創(chuàng)建或改變這些關(guān)聯(lián)。

  七、 總結(jié)

  桌面集成是Mustang的一個(gè)重要主題。Mustang支持這種主題的一種方式是提供一組java.awt.Desktop API。這種API允許Java應(yīng)用程序啟動(dòng)主機(jī)的默認(rèn)瀏覽器和電子郵件客戶端。另外,Java應(yīng)用程序能夠啟動(dòng)與特定的文件類型相關(guān)聯(lián)的應(yīng)用程序以打開(kāi),編輯和打印文件。盡管Java應(yīng)用程序不能操作,創(chuàng)建,或改變文件關(guān)聯(lián),但是這些桌面API確定允許Java應(yīng)用程序啟動(dòng)默認(rèn)的相關(guān)聯(lián)的應(yīng)用程序.