不支持浮點數(shù)據(jù)類型(即沒有float和double)(這實際上主要是由于CLDC所面向的設備,其硬件因其內(nèi)存限制或是基于安全性的考慮不支持浮點運算) 不支持類實例(class instance)的終結(finalization),即不存Object.finalize()方法。(注:finalization指的是java中提供的有別與garbage collection的另外的一種對象清理方法。具體可參看侯捷老師譯的Bruce Eckel的《Thinking in Java 第二版》,可以在www.jjhou.com中下載)
類文件審核過程(classfiles Verification)
CLDC要求其下層的虛擬機能夠辨別并拒絕非法的class文件。但由于CLDC本身面向小內(nèi)存消耗的小型設備這一前提。其類文件檢測機制與J2SE中定義的標準類文件審核機制還有所不同。請注意看下圖:
圖3 CLDC的類審核機制
請注意上圖所示CLDC中的預審核(preverification)過程。這是CLDC區(qū)別于通常的類文件審核過程的關鍵。如上圖所示,當源程序被編譯后,必須被預審核器預審核,然后才能被下載到目標設備上去。之所以有這一步驟,主要是為了減輕KVM中審核器的負擔,加快審核速度。就像我前文提到的,這是出于對CLDC支持的硬件的考慮(畢竟手機和PDA并沒有我們PC機那樣奔騰的“芯”呀!^-^)。這里記住這個概念就行了。具體的操作方法我會在以后講述編寫MIDlet程序時介紹。
CLDC的類庫API
如果不嚴格的說,CLDC的類庫是J2SE的一個小的子集,有以下這種近似關系成立,即J2SE包含CDC包含CLDC。但要注意CLDC并未完全包含于J2SE,它還包括一些專門針對無線設施的類。所以可以把CLDC的類庫簡要劃分為兩部分:從J2SE繼承的類和CLDC專有的類。
1. 從J2SE繼承的類
這部分包括三個package,即java.lang,java.util和java.io。注意即使這些從J2SE繼承的的類,也是大大“縮了水”的。例如java.util的類與接口由J2SE的47個縮減到10個。另外兩個也有相應縮減。系統(tǒng)類 From java.lang: Object, Class, Runtime, System, Thread, Runnable, String, StringBuffer, Throwable數(shù)據(jù)類型類 From java.lang: Boolean, Byte, Short, Integer, Long, Character容器類 (container class) From java.util: Vector, Stack, Hashtable, EnumerationI/O 類 From java.io: InputStream, OutputStream, ByteArrayInputStream, ByteArrayOutputStream, DataInput, DataOutput, DataInputStream, DataOutputStream, Reader, Writer, InputStreamReader, OutputStreamWriter, PrintStream日期與時間類 From java.util: Calendar, Date, TimeZone附加的實用類 java.util.Random, java.lang.Math異常類 (Exception Classes) From java.lang: Exception, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException, RuntimeException, ArithmeticException, ArrayStoreException, ClassCastException, IllegalArgumentException, IllegalThreadStateException, NumberFormatException, IllegalMonitorStateException, IndexOutOfBoundsException, ArrayIndexOutOfBoundsException, StringIndexOutOfBoundsException, NegativeArraySizeException, NullPointerException, SecurityException From java.util: EmptyStackException, NoSuchElementException From java.io: EOFException, IOException, InterruptedException, UnsupportedEncodingException, UTFDataFormatException錯誤類 From java.lang: Error, VirtualMachineError, OutOfMemoryError
2. CLDC專有的類 這里sun提供了javax.microedition.io包。用它來提供與外界溝通的橋梁。(注:就像在J2SE中我們利用java.net.*)注意,所有的連接都由javax.microedition.Connector創(chuàng)建。例如: 你可以打開一個http連接通過Connector.open("http://www.") javax.microedition.io的接口包括: Connection, InputConnection, OutputConnection, StreamConnection, ContentConnection, DatagramConnection, StreamConnectionNotifier 這里再說明一點,實際上你也可以看出,CLDC所包括的類庫,并不足以開發(fā)應用程序。例如,沒有UI界面的支持等。這也正是CLDC的設計理念。CLDC本身就從未想過要解決所有的問題。這些問題要靠其上層的Profile-MIDP來解決。例如javax.microedition.lcdui就是定義在MIDP中的UI包。
MIDP對應的設備特性
由于MID這類設備,在屏幕、內(nèi)存、處理器等問題上有諸多限制,在手機或是PDA等MID上開發(fā)應用程序必須要考慮一些技術上的特殊點。
下面給出一些MID設備的特性:
顯示(display):96x54 (最小屏幕尺寸),1bit(最小色深,單色)
輸入設備: “one-handed keyboard”(指ITU-T手機鍵盤),“two-handedkeyboard”(指標準鍵盤,即QWERTY鍵盤),觸摸屏。
內(nèi)存分配:128kb(MIDP組件);8kb(應用程序生成的Persistent data,關于Persistent data,我會在將來講RMS時,詳細說明,這里有個概念就行了);32kb(java runtime環(huán)境)
網(wǎng)絡: 雙向的,無線的,間斷的,帶寬有限的網(wǎng)絡
內(nèi)核(kernel):至少要能運行KVM。
還有很多軟件上的特性,如讀寫non-volatile內(nèi)存(就是掉電后不會失去內(nèi)容的內(nèi)存,如flash)。讀寫無線設備接口的API,等等。除了上訴技術上的問題,你還得注意你的程序要簡單易用且穩(wěn)定可靠。尤其是可靠性,你開發(fā)的是通訊設備,用戶是不能忍受程序有什么紕漏而影響到通話的。你要牢記這一點。
MIDP的類庫API
如前文所述,sun在CLDC之上定義了MIDP(Mobile Information Device Pro-file)層,用以提供對UI、永久存儲介質(persistinace storage)、和網(wǎng)絡等更高層的(相對于CLDC)支持。那么,讓我們來具體看看MIDP的類庫。 MIDP由四個javax.microedition包組成,它們包括:
javax.microedition.rms 關于永久存儲介質(注:rms是Record Management System的縮寫)
javax.microedition.midlet 定義了MIDlet的框架,以及MIDlet與環(huán)境的交互。
javax.microedition.io 網(wǎng)絡支持
javax.microedition.lcdui UI(User Interface)(注:UI分為high-level和low-level兩種API。)
注:如果在加上語言和實用類(java.lang和java.util)則有六個。
編寫并運行一個MIDlet
MIDPlet簡介 MIDP中定義的應用程序稱為MIDlet。任何一個MIDlet都是javax.microedition.midlet.MIDlet的子類,必須繼承自javax.microedition.midlet.MIDlet。這很顯而易見。我們在J2SE中編過Applet,Applet就必須繼承自java.applet.Applet。是不是很類似。請看下圖,說明了MIDlet的繼承體系。
圖4 MIDlet的繼承體系
一個簡單的MIDlet
//HelloWorld.java,一個最簡單的MIDlet程序。 import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class HelloWorld extends MIDlet implements CommandListener { private Command exitCommand; private TextBox tb; public HelloWorld(){ exitCommand =new Command("Exit",Command.EXIT,1); tb =new TextBox("Hello MIDlet","Hello,World!",15,0); tb.addCommand(exitCommand); tb.setCommandListener(this); } protected void startApp(){ Display.getDisplay(this).setCurrent(tb); } protected void pauseApp(){ } protected void destroyApp(boolean u){ } public void commandAction(Command c,Displayable d){ if (c ==exitCommand){ destroyApp(false); notifyDestroyed(); } } }
該程序顯示“HelloWorld”字符串。程序本身非常簡單,如果你熟悉JavaApplet編程的話,你會發(fā)現(xiàn)與Applet很類似。好,我們先把它編譯,運行一下看看。
編譯之前的準備:
硬件:因為是Java,所以內(nèi)存最少有128MB以上,CPU最好是PII或更高。
平臺:最好是win2000,win98的中文顯示會有問題,解決起來比較麻煩。Linux我沒有試過,英文我想應該都沒什么問題,但中文顯示我估計可能會有些麻煩。如果有試過的朋友,請一定告訴我。
SDK:你需要有(下述軟件都可在sun網(wǎng)站上免費下載) 1.Java 2 SDK 1.3或以上。 2.J2ME Wireless Toolkit 1.0.3 以下可選: sun的IDE:Forte for java。(如果你上的是寬帶,或不是自己付網(wǎng)費的話 ^-^)
下載好后,先安裝JDK1.3,(具體關于安裝和設置我就不詳述了,想來大家都很清楚。如果您不清楚,請參考相應資料。)在安裝J2ME Wireless Toolkit 1.0.3,它會自動找到您的JDK1.3,并作相應設置。如果您不用命令行的話,環(huán)境變量也不用設置直接可在圖形界面上運行,非常方便。關于環(huán)境變量的設置請自己參考手冊。
編譯的步驟 如果你按上述已經(jīng)安裝好JDK和J2mewtk,請按以下步驟編譯,實際上是非常簡單的:
(注:下述默任JDK安裝在c:\JDK1.3,J2ME Wirless Toolkit 安裝在c:\J2mewtk\)
1. 開始->程序->J2ME Wirless ToolKit 1.0.3->KToolBar
2. 這時會出現(xiàn)"J2ME Wirless ToolKit"窗口,點"New project"按鍵,在Project name項填:HelloWorld;在MIDLet Class Name填:HelloWorld。點擊Creat project。
3. 這時會出現(xiàn)Setting for project對話窗,采取默認即可。點擊ok。
這時請注意,因為沒有down sun的IDE,所以你要手工把你事先編輯好的HelloWorld.java放置在c:\j2mewtk\apps\HelloWorld\src\目錄下。這時,按“Build”鍵,如果一切正常,將提示編譯完畢。這時在device下拉選單中選定你想要的設備,再按“run”鍵,終于大功告成。你可以多選幾個不同設備,感受一下,不錯吧。
MIDlet的lifecycle
我們已經(jīng)成功編譯并運行了一個簡單的MIDlet程序?,F(xiàn)在,讓我們來分析一下MIDlet的結構,了解一下它的Lifecycle。
通過對源程序的觀察,我們可以發(fā)現(xiàn)MIDlet程序的運行是由startApp(),pauseApp()和destroyApp()這3個方法控制的。它們在javax.microedition.midlet .MIDlet中定義。所有的MIDlet都必須有這3個方法。
顧名思義startApp()方法用于標志一個MIDlet的開始執(zhí)行。不過這里要注意一點,與HelloWorld程序的constrctor不同。startApp()不光是在初始化完一個MIDlet時執(zhí)行,只要該MIDLet被從Paused態(tài)激活(變?yōu)锳ctive態(tài)),startApp()就會被調用。
pauseApp()方法標志著MIDlet進入Pause態(tài)。而destroyApp()方法標志著MIDlet進入destroyed態(tài)。(注意:這里嚴格的講應該說成:方法被調用并成功返回標志著...) 看完上面的描述,大家可能會滿頭霧水,又是方法,又是狀態(tài)的,什么跟什么嗎?這還得從MIDlet的執(zhí)行機制講起。
MIDlet的執(zhí)行是通過Application Management software來管理的。這玩意兒是處在操作系統(tǒng)級別上來管理MIDlet運行的底層機制的總稱,所謂MIDlet state(MIDlet狀態(tài))就是它一手操辦,控制管理的。MIDlet state確保了AMS隨時可以消滅該MIDlet,同時MIDlet也有辦法進入一個Pause態(tài),并可再次激活。
MIDlet State 分為Paused,Active,destroyed三種。當AMS創(chuàng)生一個新的MIDlet實體時,對應于MIDlet,表現(xiàn)為其constructor被調用,進入Paused狀態(tài)。當所有的準備工作都做好后,AMS判斷現(xiàn)在MIDlet可以運行了,于是調MIDlet.startApp()方法。進入Active態(tài)。當AMS決定要把MIDlet轉入Paused態(tài),就會調用MIDlet.pauseApp()方法,MIDlet就會暫停執(zhí)行,通常Paused態(tài)會用于釋放所占資源。當AMS判斷MIDlet不再需要,就會調用MIDlet.destroyApp(),MIDlet被消滅。
請注意我上述是站在AMS的角度在談AMS如何控制MIDlet的狀態(tài)改變。程序員也可請求MIDlet的狀態(tài)的變換,通過調用resumeRequest, notifyPaused, notifyDestroyed這三個方法。
我們的HelloWorld程序先把destroyApp()的unconditional值置為false,拋出一個MIDletStateChangeException 異常,表示MIDlet這時還不想被destroy。notityDestroyed()通知AMSMIDlet進入destroyed態(tài)。具體的細節(jié)請參閱MIDP API文檔。
下面給出一個MIDlet的最簡單的狀態(tài)流程:
//FlowMIDlet.java MIDlet的最簡單的狀態(tài)流程 import javax.microedition.midlet.*; public class FlowMIDlet extends MIDlet { public void startApp() { System.out.println( "In startApp..." ); pauseApp(); } public void pauseApp() { System.out.println( "In pauseApp..." ); destroyApp( true ); } public void destroyApp( boolean unconditional) { System.out.println( "In destroyApp..." ); } }
MIDlet的狀態(tài)的改變可以用下圖表示:
這里還要說幾句閑話,關于AMS,其作用不止是控制MIDlet的運行狀態(tài)。它實際上際上負責了MIDlet的整個運行機制。關于AMS進一步的描述,請參見王森老師的文章-“利用Java撰寫手機應用-Java Application Manager篇”(《程序員》,12期,2001)。注意其中JAM就是AMS。
好了,J2ME(CLDC/MIDP)的簡介就算是結束了。把帖子轉成html實在是一項很累的工作。試過這后我才明白這個道理。如果您看過之后,有那么一點幫助的話,那就算是對我最大的報償了。如果您覺得文章有什么錯漏之處,或是有什么感想,歡迎給我E-mail 。