這篇文章介紹怎樣在標準Java(Java SE,也稱作J2SE)平臺上用Headless模式。
Headless模式是在缺少顯示屏、鍵盤或者鼠標時的系統配置。聽起來不可思議,但事實上你可以在這中模式下完成不同的操作,甚至是用圖形數據也可以。
哪里才能用到此模式呢?想想你的應用不停的生成一張圖片,比如,當用戶每次登陸系統是都要生成一張認證圖片。當創建圖片時,你得應用既不需要顯示器也不需要鍵盤。讓我們假設一下,現在你的應用有個主架構或者專有服務器,但這個服務沒有顯示器,鍵盤或者鼠標。理想的決定是用環境的大量視覺計算能力而不是非視覺特性。在Headless模式下生成的圖片可以傳遞到Headful系統進行更深層次渲染。
在java.awt.toolkit和java.awt.graphicsenvironment類中有許多方法,除了對字體,圖像和打印的操作外還有調用顯示器,鍵盤和鼠標的方法。但是有一些類中,比如Canvas 和 Panel,可以在headless模式下執行。在J2SE 1.4平臺之后就提供了對Headless模式的支持。
注:這篇文章重點講的是Java SE6 平臺版本的文檔。任何API的增加或其他增強Java SE平臺的規范是由JSR270專家組(JSR 270 Expert Group.)的審查和批準。
Toolkit
java.awt.Toolkit類是Abstract Window Toolkit (AWT)的 所有實現類的抽象父類。Toolkit的子類用于把各種AWT組件綁定到特定的本地toolkit實現上去。
如果顯示設備,鍵盤或鼠標不支持的話,很多組件都會受影響。一個合適的類構造器應當拋出一個HeadlessException異常:
- Button
- Checkbox
- Choice
- Dialog
- FileDialog
- Frame
- Label
- List
- Menu
- MenuBar
- MenuItem
- PopupMenu
- Scrollbar
- ScrollPane
- TextArea
- TextField
- Window
這種重量級的組件需要有一個操作系統級別上對等的圖形函數來支持它,在headless的機器上它們將不能正常工作。
與Canvas、Panel和Image組件相關的組件不需要拋出HeadlessException異常,因為這些組件在操作系統級別上的對等圖形函數可以使用空函數,然后作為輕量級組件來處理。
一個Headless的toolkit也會把Java組件綁定到本地資源上去,但是它只有在資源中不包含顯示設備或輸入設備時才會這樣做。
Graphics Environment
java.awt.GraphicsEnvironment類是一個抽象類,它描述了在給定平臺中,可以在Java技術中使用的由GraphicsDevice對象和Font對象組成的集合。該GraphicsEnvironment中的資源可以是本地的也可以是遠程設備。GraphicsDevice對象可以是顯示器,打印機或者圖形緩存等,并且它們是Graphics2D 繪制函數的目標。每一個GraphicsDevice都有許多與之關聯的GraphicsConfiguration對象。這些對象指定了不同的配置環境,在這些配置環境中可以使用GraphicsDevice。
Table 1 顯示GraphicsEnvironment 方法,檢查Headless模式支持
Table 1. Headless 模式方法
注意:isHeadless()方法檢查特定的系統屬性,java.awt.headless而不是系統的硬件配置.
HeadlessException 拋出的代碼,這取決于display device、keyboard、mouse在一個環境稱為不支持任何這些.唯一的例外是來自一個UnsupportedOperationException,本身就是來源于一個RuntimeException.
設置 Headless模式
使用Headless模式操作,您必須首先了解如何檢查和設置系統屬性與此相關的模式。此外,你必須了解如何創建一個默認的工具包使用工具箱的無頭實現類.
系統屬性配置
為了啟用headless模式,需要使用setProperty()方法去設置相應的系統屬性。本方法可以讓你用期望的值來設置系統屬性。
1
|
System.setProperty( "java.awt.headless" , "true" ); |
上面的代碼中,java.awt.headless是一個系統屬性,true是我們設定的值。
如果你想在一個相同的程序中使用headless和傳統環境,你可以使用下面的命令行來完成:
1
|
java -Djava.awt.headless= true |
創建默認Toolkit
如果名字為java.awt.headless的系統屬性被設置為true,那么headless工具包就會被使用。接下來使用getDefaultToolkit()方法來創建一個headless toolkit的實例:
1
|
Toolkit tk = Toolkit.getDefaultToolkit(); |
Headless模式檢查
要檢查Headless模式的可用性,使用GraphicsEnvironment類的isHeadless()方法:
1
2
3
|
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); boolean headless_check = ge.isHeadless(); |
該方法檢查java.awt.headless系統屬性。如果這個屬性有一個為true的值,那么就會從工具包和依賴于一個顯示器,鍵盤,鼠標的GraphicsEnvironment類的區域中拋出一個HeadlessException。
在Headless模式中操作
設置好headless模式并創建一個headless工具包的實例后,您的應用程序可以執行以下操作:
- 創建輕量級組件,如Canvas,Panel,和Swing組件,除了top級別.
- 收集關于可用的字體、字體指標和字體設置的信息
- 設置顏色來渲染文本和圖形
- 創造和獲取圖像,為渲染準備圖片
- 使用java.awt.PrintJob, java.awt.print.*, 和 javax.print.* 類進行打印。
- 發出"嗶嗶"音頻。
Canvas(畫布)
下面的代碼會在屏幕上繪制出一個空白的矩形區域,你可以在上面繪制線條??梢允褂肅anvas類創建一個新的Canvas組件。
1
2
3
4
5
6
7
8
9
|
final Canvas c = new Canvas() { public void paint(Graphics g) { Rectangle r = getBounds(); g.drawLine( 0 , 0 , r.width - 1 , r.height - 1 ); g.drawLine( 0 , r.height - 1 , r.width - 1 , 0 ); } }; |
Fonts(字體)
這段代碼顯示了怎么使用Font類畫一個文本字符串并設置文字的字體。Graphics對象是用來繪制這個字符串的。
1
2
3
4
5
|
public void paint(Graphics g) { g.setFont( new Font( "Arial" , Font.ITALIC, 12 )); g.drawString( "Test" , 32 , 8 ); } |
Colors
這段代碼顯示了如何使用指定的紅,綠,藍的值來設置一條線的顏色。Graphics對象是用來繪制這條線的。
1
2
3
4
5
|
public void paint(Graphics g) { g.setColor( new Color( 255 , 127 , 0 )); g.drawLine( 0 , r.height - 1 , r.width - 1 , 0 ); } |
Images
在下面的代碼中,javax.imageio.ImageIO類的使用read()方法對圖1所示的grapefruit.jpg文件進行解碼,并返回一個緩存圖片。
1
2
3
4
5
6
7
8
9
10
|
Image i = null ; try { File f = new File( "grapefruit.jpg" ); i = ImageIO.read(f); } catch (Exception z) { z.printStackTrace(System.err); } |
圖1。grapefruit.jpg圖像文件
這段代碼演示了如何打印已經準備好的畫布,你可以使用paint方法自定義打印機的的默認畫面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintable( new Printable() { public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex > 0 ) { return Printable.NO_SUCH_PAGE; } ((Graphics2D)g).translate(pf.getImageableX(), pf.getImageableY()); // Paint canvas. c.paint(g); return Printable.PAGE_EXISTS; } }); |
Beep
下面的這段代碼展示了如果使用 Toolkit類的beep方法發出嘟嘟聲。
1
2
|
Toolkit tk = Toolkit.getDefaultToolkit(); tk.beep(); |
使用Headless模式簡單例子
以下的HeadlessBasics例子運用了文章中描述的所有功能。
要運行這個的例子,需要用javac對下面的代碼進行編譯。復制grapefruit.jpg圖片文件到HeadlessBasics類所在的目錄下面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
import java.awt.*; import java.io.*; import java.awt.print.*; import javax.imageio.*; public class HeadlessBasics { public static void main(String[] args) { // Set system property. // Call this BEFORE the toolkit has been initialized, that is, // before Toolkit.getDefaultToolkit() has been called. System.setProperty( "java.awt.headless" , "true" ); // This triggers creation of the toolkit. // Because java.awt.headless property is set to true, this // will be an instance of headless toolkit. Toolkit tk = Toolkit.getDefaultToolkit(); // Standard beep is available. tk.beep(); // Check whether the application is // running in headless mode. GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); System.out.println( "Headless mode: " + ge.isHeadless()); // No top levels are allowed. boolean created = false ; try { Frame f = new Frame( "Frame" ); created = true ; } catch (Exception z) { z.printStackTrace(System.err); created = false ; } System.err.println( "Frame is created: " + created); // No other components except Canvas and Panel are allowed. created = false ; try { Button b = new Button( "Button" ); created = true ; } catch (Exception z) { z.printStackTrace(System.err); created = false ; } System.err.println( "Button is created: " + created); // Canvases can be created. final Canvas c = new Canvas() { public void paint(Graphics g) { Rectangle r = getBounds(); g.drawLine( 0 , 0 , r.width - 1 , r.height - 1 ); // Colors work too. g.setColor( new Color( 255 , 127 , 0 )); g.drawLine( 0 , r.height - 1 , r.width - 1 , 0 ); // And fonts g.setFont( new Font( "Arial" , Font.ITALIC, 12 )); g.drawString( "Test" , 32 , 8 ); } }; // And all the operations work correctly. c.setBounds( 32 , 32 , 128 , 128 ); // Images are available. Image i = null ; try { File f = new File( "grapefruit.jpg" ); i = ImageIO.read(f); } catch (Exception z) { z.printStackTrace(System.err); } final Image im = i; // Print system is available. PrinterJob pj = PrinterJob.getPrinterJob(); pj.setPrintable( new Printable() { public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex > 0 ) { return Printable.NO_SUCH_PAGE; } ((Graphics2D)g).translate(pf.getImageableX(), pf.getImageableY()); // Paint the canvas. c.paint(g); // Paint the image. if (im != null ) { g.drawImage(im, 32 , 32 , 64 , 64 , null ); } return Printable.PAGE_EXISTS; } }); try { pj.print(); } catch (Exception z) { z.printStackTrace(System.err); } } } |
圖2顯示了這個例子中的打印輸出結果。
圖2。HeadlessBasics的打印輸出。
此外,你可以看到以下的信息:
1
2
3
4
5
6
7
8
9
10
11
12
|
Headless mode: true java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(Unknown Source) at java.awt.Window.<init>(Unknown Source) at java.awt.Frame.<init>(Unknown Source) at HeadlessBasics.main(HeadlessBasics.java:24) Frame is created: false java.awt.HeadlessException at java.awt.GraphicsEnvironment.checkHeadless(Unknown Source) at java.awt.Button.<init>(Unknown Source) at HeadlessBasics.main(HeadlessBasics.java:39) Button is created: false |
注:出于演示的目的,最初的代碼會導致此應用程序拋出2個java.awt.HeadlessExceptions異常。
作為上一種方式的替代,你可以把標準輸出信息放到一個文件中,然后把文件打印出來。在這種情況下,使用下面的命令行來運行這個例子:
1
|
java HeadlessBasics 2> standard_output.txt |
把現有的應用程序轉換為Headless模式。
你怎么把現有的應用程序轉換為可執行的headless模式?要執行此轉換的最有效的方法是分析你的源代碼以確定任何的功能都是依賴于Headless模式的。換句話說,要實現相同的功能,你必須找到那些會拋出HeadlessException異常的類和方法,然后使用獨立的headless模式替換這些類和方法。
你可以使用Java SE 6 API說明來判斷一個特定的類或方法是否支持headless模式。如果一個特定的組件不支持headless模式,你的程序需要捕獲的唯一的異常是HeadlessException。它會在其它可能的異常之前被拋出。這也是為什么在本節的代碼示例"舉例: 使用Headless模式"中,沒有什么特殊的必要性來捕獲其它異常。
你肯定會發現其它有用的方法來使用headless模式帶來的好處。我們希望本文能幫你完成此項任務,在Java SE平臺中玩出一片新天地。