xlet

Sun於2002年9月發布了J2ME Personal Profile 1.0,但和MIDP不同的是,Personal Profile建立在CDC(Connected Device Configuration)的基礎上。CDC提供了一個功能完整的Java 2虛擬機,和CLDC相比,CDC要求有更高的記憶體和更可靠的網路連線。

基本信息

Java手機核心技術MIDP建立在CLDC(Connected Limited Device Configuration)的基礎上。Sun於2002年9月發布了J2ME Personal Profile 1.0,但和MIDP不同的是,Personal Profile建立在CDC(Connected Device Configuration)的基礎上。CDC提供了一個功能完整的Java 2虛擬機,和CLDC相比,CDC要求有更高的記憶體和更可靠的網路連線。

Personal Profile包含了完整的AWT API集,支持圖形用戶接口(GUI),支持Applet和Xlet,為高端PDA套用提供了一個完整的開發、運行環境。

Xlet套用模型繼承自Personal Basis Profile,是Personal Profile最重要的特色之一。什麼是Xlet呢?就象J2SE環境下的Applet,Xlet也是一種必須在宿主(應用程式管理器)之內運行的套用。也就是說,Xlet本身不包含main()方法,不能作為獨立的應用程式運行。但是,Xlet總是實現一組讓應用程式管理器控制其狀態的接口。

和J2SE領域的Applet相比,Xlet在J2ME領域的地位可能重要得多——構想一下,PDA將能夠下載各種第三方的Xlet套用,輕鬆實現PDA功能的動態擴展;甚至一個Xlet可以通過互操作機制提供對其他Xlet的服務,從而開發出由多個模組化Xlet構成的類似客戶機/伺服器體系的複雜套用。

生命周期

每一個Xlet必須實現javax.microedition.xlet.Xlet接口定義的四個方法:public interface Xlet {

public void initXlet(XletContext ctx) throws XletStateChangeException;

public void startXlet() throws XletStateChangeException;

public void pauseXlet();

public void destroyXlet(boolean unconditional) throws

XletStateChangeException;

}

和Applet一樣,對於Xlet來說生命周期也是一個很重要的概念。Xlet的應用程式管理器正是通過上述四個方法來控制Xlet的狀態。要理解Xlet編程,首先必須理解Xlet的生命周期。

Xlet的生命周期包括下面四種狀態:

一 裝入(Loaded):已經從本地存儲器或網路裝入Xlet,且已調用其不帶參數的構造函式。此時如果調用Xlet的initXlet()方法,Xlet可以轉入暫停狀態。

二 暫停(Paused):Xlet已初始化,且已做好激活的準備,相當於進程的“已準備好”狀態——已經做好了隨時在CPU上運行的準備。這時如果調用Xlet的startXlet()方法,它就進入活動狀態。

三 活動(Active):Xlet正在正常運行。如果調用Xlet的destoryXlet()方法,則它進入“拆除”狀態,如果調用pauseXlet()方法,則進入暫停狀態。

四 拆除(Destroyed):這是Xlet的終止狀態。進入已拆除狀態的Xlet不能再轉入其他狀態,Xlet占用的所有資源將被回收。

Xlet可以從任何其他狀態轉入拆除狀態。圖1顯示了Xlet各種狀態的關係。

圖1

實現接口

javax.microedition.xlet.Xlet接口定義的方法也稱為“生命周期方法”。必須注意的是,用戶應用程式(包括Xlet本身)不應該直接調用生命周期方法——即使強行調用,Xlet的狀態也不會改變。生命周期方法應當由管理器調用,管理器通過生命周期方法來通知Xlet改變其狀態。從這個意義上來看,生命周期方法就象是一種事件句柄。

initXlet(XletContext ctx)

管理器裝入並實例化Xlet之後,調用initXlet()方法初始化Xlet。initXlet()的參數是一個XletContext,這個參數很重要,它是Xlet獲取其運行上下文環境的唯一途徑。XletContext類不僅提供了一些方法來提取傳遞給Xlet的參數,而且還有一個可供Xlet放置AWT組件的容器,更重要的是,Xlet本身還可以通過XletContext發出狀態變換命令以及與其他Xlet通信。

startXlet(),pauseXlet()

startXlet()方法告知Xlet開始提供服務,並將Xlet轉入活動狀態。pauseXlet()的功能恰好相反:要求Xlet停止服務,並將其轉入暫停狀態。

initXlet()和startXlet()主要的不同之處在於:前者只能調用一次,對於後者,當管理器希望Xlet進入或者重新進入活動狀態時可以多次調用。因為startXlet()可能被多次調用,所有只能執行一次的初始化工作應當在initXlet()而不是startXlet()中進行。

通常而言,類似startXlet()方法的位置是放置某些業務邏輯的好地方。但是,Personal Profile規範指出Xlet接口中定義的所有方法只能用於狀態轉換,Xlet套用管理器要求這些方法儘快返回。如果生命周期方法沒有在一定的時間內(與具體的套用實現有關)返回,管理器將認為遇到了錯誤,直接拆除Xlet。因此,不應該在任何生命周期方法內放置長時間執行的業務邏輯,如果Xlet需要提供某種可反覆啟動、停止的服務,可以考慮用一個專用的執行緒來提供服務,生命周期方法通過與該服務執行緒的通信來控制服務的啟動、停止,例如由生命周期方法設定一個服務啟動/停止的標記。

destroyXlet(boolean unconditional)

該方法通知Xlet結束運行,轉入拆除狀態。Xlet應當釋放所有的資源。參數unconditional由管理器設定,表示是否要無條件地拆除Xlet。如果unconditional是false,Xlet可以拋出一個StateChangeException異常,表示自己不想被拆除——但是,是否接受Xlet請求最終還是由管理器決定。也就是說,雖然Xlet有權合法地拋出StateChangeException異常,但最終決定其命運的還是管理器。如果管理器接受了Xlet要求不拆除的請求,它會給Xlet一些時間,一定的時間後再次調用destory()方法,這次unconditional一般會設定成true。當unconditional參數是true時,管理器將忽略任何XletStateChangeException異常,一旦destoryXlet()返回就直接拆除Xlet。

可以從生命周期方法拋出的異常有兩種:XletStateChangeException,未被捕獲的RuntimeException或錯誤。

如果生命周期方法拋出了未處理的RuntimeException或錯誤,管理器將立即調用Xlet的destoryXlet(true)方法,將Xlet拆除。因此,Xlet應當捕獲所有“正常的”(原因已知的)RuntimeException或錯誤,避免將RuntimeException直接拋給管理器從而導致Xlet被拆除。相對而言,XletStateChangeException可以由Xlet有意地拋出,表示Xlet尚未做好改變狀態的準備。

轉換狀態

如前所述,Xlet接口中定義的生命周期方法是供管理器通知Xlet轉換狀態用的。那么,如果Xlet本身想要轉換狀態,例如用戶想要結束Xlet(但不想等待管理器發出停止Xlet的命令),又該如何進行呢?

XletContext提供了三個方法讓Xlet發出轉換狀態通知,分別是:notifyDestroyed(),notifyPaused(),和resumeRequest()。notifyDestroyed()告訴管理器Xlet想要結束運行。就象調用destroyXlet()一樣,在調用notifyDestroyed()之前Xlet應當釋放所有的資源。調用notifyDestroyed()之後Xlet將立即無條件地轉入拆除狀態。

notifyPaused()將Xlet轉入暫停狀態。Xlet可以通過調用notifyPaused()為其他Xlet讓出運行資源。一般地,後繼的resumeRequest()調用能夠把Xlet返回到活動狀態,但正如該方法名稱所示的,調用resumeRequest()只是發出了一個請求,而該請求能否被接受是沒有辦法保證的,最終要由管理器來決定Xlet是否可以返回、何時返回活動狀態——Xlet可能要等待一段較長的時間才能返回活動狀態,可能根本不能返回活動狀態;或者,由於缺少資源,管理器可能調用destroyXlet()來結束一個暫停的Xlet。

開發實例

下面通過一個簡單的實例示範Xlet的開發。這個Xlet是一個簡單的數字時鐘,顯示出當前的小時、分鐘、秒,用戶可以隨時暫停或終止時鐘。這個時鐘可以通過它本身的按鈕控制,也可以通過管理器調用Xlet的生命周期方法來控制。Sun為Personal Profile提供了一個參考實現,其管理器稱為XletRunner()。如果在XletRunner中運行時鐘Xlet,用戶可以通過XletRunner的選單來控制時鐘狀態,例如要暫停時鐘,除了點擊時鐘的“暫停”按鈕,還可以選擇XletRunner的ClockXlet選單並選擇pause。

下面是時鐘Xlet的完整代碼。

import javax.microedition.xlet.*;

import java.util.*;

import java.awt.*;

import java.awt.event.*;

public class ClockXlet implements Xlet,ActionListener {

TextField display;

MyClock clock;

Button pauseButton = new Button("暫停");

Button stopButton = new Button("停止");

Button resumeButton = new Button("繼續");

XletContext context;

public void initXlet(XletContext ctx) throws

XletStateChangeException{

Container c;

context = ctx;

try {

c = ctx.getContainer();

} catch (UnavailableContainerException e) {

throw new XletStateChangeException(e.getMessage());

}

display = new TextField(30);

clock = new MyClock(display);

pauseButton.addActionListener(this);

resumeButton.addActionListener(this);

resumeButton.setEnabled(false);

stopButton.addActionListener(this);

c.setSize(200,200);

c.setVisible(true);

c.add(display);

c.add(pauseButton);

c.add(resumeButton);

c.add(stopButton);

clock.start();

}

public void startXlet() {

clock.setPaused(false);

resumeButton.setEnabled(false);

pauseButton.setEnabled(true);

}

public void pauseXlet() {

clock.setPaused(true);

resumeButton.setEnabled(true);

pauseButton.setEnabled(false);

}

public void destroyXlet(boolean unconditional) {

clock.setStopped(true);

}

public void actionPerformed(ActionEvent e) {

if (e.getSource() == stopButton) {

clock.setStopped(true);

context.notifyDestroyed();

} else if (e.getSource() == pauseButton) {

clock.setPaused(true);

resumeButton.setEnabled(true);

pauseButton.setEnabled(false);

context.notifyPaused();

} else if (e.getSource() == resumeButton) {

context.resumeRequest();

} } }

class MyClock extends Thread {

boolean paused,stopped;

TextField display;

public MyClock(TextField t) {

display = t;

}

String getTime() {

Calendar rightNow = Calendar.getInstance();

String hour = String.valueOf(rightNow.get(Calendar.HOUR_OF_DAY));

String min = String.valueOf(rightNow.get(Calendar.MINUTE));

if (min.length() == 1) {

min = "0" + min;

}

String sec = String.valueOf(rightNow.get(Calendar.SECOND));

if (sec.length() == 1) {

sec = "0" + sec;

}

return hour + ":" + min + ":" + sec;

}

public synchronized boolean isStopped() {

return stopped;

}

public synchronized void setStopped(boolean value) {

stopped = value;

notifyAll();

}

public synchronized boolean isPaused() {

return paused;

}

public synchronized void setPaused(boolean value) {

paused = value;

notifyAll();

}

public void run() {

while (!isStopped()) {

try {

if (!isPaused()) {

Thread.sleep⑴;

display.setText(getTime());

} else {

synchronized (this) {

wait();

}

}

} catch (InterruptedException e) {

} } } }

首先來看看MyClock類,它的主要功能是在一個TextField中顯示出當前時間。MyClock類擴展了Thread類,其主要功能在run()方法中實現。MyClock類用兩個標記來表示是否出現了暫停或停止請求;如果沒有這類請求,時鐘將正常運行,每隔1秒刷新當前時間。如果已出現暫停請求,時鐘轉入等待狀態,直至暫停標記被清除再次喚醒執行緒。如果出現了停止時鐘的請求,執行緒結束運行。

MyClock由ClockXlet控制。initXlet()方法首先創建一個供MyClock使用的TextField,以及幾個用來控制時鐘的按鈕。然後,它啟動MyClock的執行緒。這裡的startXlet()和pauseXlet()都很簡單,很快就可以返回(記住,生命周期方法應當儘快返回),其主要功能就是設定/取消MyClock的暫停標記,從而起到暫停/繼續執行MyClock執行緒的作用。destroyXlet()方法設定MyClock的停止標記,從而終止執行緒執行。

actionPerformed()方法把用戶的動作轉換成對管理器的狀態變換請求。點擊時鐘的“暫停”按鈕暫停時鐘運行,同時它還通過調用XletContext.notifyPaused()向管理器傳送要求進入暫停狀態的請求。點擊“繼續”按鈕時Xlet傳送一個要求返回活動狀態的請求,如果管理器接受了請求,它就會調用Xlet的startXlet()方法,時鐘繼續運行。對於點擊“停止”按鈕的事件,處理方式也相似。

我們可以用Sun的Personal Profile參考實現提供的管理器XletRunner來測試ClockXlet。首先要從(由於百度不能提供網址連結,故去掉http!去sun java 下載!百度真垃圾,偷別人的東西,還不允許給他人打個標籤)products/ personalprofile/download.html下載管理器。Sun在下載頁面中聲明該版本的運行環境(當前唯一可免費下載的版本)只能用於Linux 2.2或更高的機器上,其他版本屬於商業軟體。但經過測試,我發現它在XP下也運行得很好,如圖2所示。

以Windows XP環境為例,從Sun網站下載得到的是一個ZIP檔案,解開壓縮,不必理會它的運行需求說明,只要將j2me-pp1.0\lib裡面的內容全部複製到Java源檔案所在目錄。用javac -classpath personal.jar ClockXlet.java命令編譯。完成後,用java -cp personal.jar com.sun.xlet.XletRunner -name ClockXlet -path c:/test/ClockXlet命令就可以啟動ClockXlet。

總結:Xlet和Applet一樣,只能在管理器中運行。在Xlet編程中,狀態轉換是極其重要概念,每一個Xlet都必須實現接口規範定義的四個生命周期方法。Xlet本身不能直接調用生命周期方法,但可以通過XletContext請求變換自身的狀態。

相關詞條

相關搜尋

熱門詞條

聯絡我們