在默認(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)用程序.