編程|多線程|網路
摘 要 介紹並分析了J2ME的通用網路連接架構(GCF),並以此為基礎,指出了在J2ME網路連接編程中存在的問題,並提出了通過構建多線程的兩種解決方案。
關鍵詞 J2ME GCF多線程 網路連接
引言
J2ME(Java 2 Micro Edition)是Java 2的一個組成部分,它與J2SE、J2EE並稱。J2ME是一種高度最佳化的Java運行環境,主要針對消費類電子裝置的,例如蜂窩電話、可視電話、數字機頂盒和汽車導航系統等等。即J2ME是為消費電子產品和手持功能量身定製的Java專用版本。
J2ME的出現使開發跨平台的消費類電子產品的應用軟體成為可能。Java語言的與平台無關的特性移植到小型電子裝置上,允許移動無線裝置之間共用應用程式。它提供了基於HTTP的進階Internet協議,使行動電話能以Client/Server方式直接存取Internet的全部資訊,從而使得不同的Client訪問不同的資源。
在將來的無線通訊時代中,大量的網路應用程式將被開發出來去滿足無線移動通訊的要求,而要充分的發揮無線移動通訊裝置的通訊能力,J2ME網路編程就變得尤為重要。那麼為了高效地進行網路編程,就要利用Java語言的多線程編程機制。
1、J2ME的網路連接架構(GCF)
通用串連架構(Generic Connection Framework,GCF)為資源有限的裝置提供了一個可擴充的、通用的I/O 架構。GCF 是一組在 javax.microedition.io 包中定義的介面。圖 1 顯示了 GCF 的類階層。
圖1 GCF 的類階層
在 GCF 中共定義了七個介面 ,其中 Connection 是最基本的連線類型。且同時提供了對資料包和流串連的支援。沿著階層向下派生出提供更多功能的介面。例如, StreamConnection 介面支援輸入資料流和輸出資料流, ContentConnection介面又擴充了 StreamConnection 介面以支援對流的內容類型、資料長度和編碼格式的確定,HttpConnection介面又擴充了ContentConnection介面以支援對於標準的HTTP請求。如在架構層規定的適用於手機或雙向傳呼機的移動資訊裝置架構MIDP(Mobile Information Device Profile)在其MIDP 1.0 規範只要求裝置支援 HTTP 連線協定,而更新的 MIDP 2.0 規範要求同時支援 HTTP 和 HTTPS,後者提供了對更安全的網路連接的支援。
2、網路編程中的多線程
由於目標裝置具有記憶體小,計算能力弱和電池供電等特點,所以如何使應用程式高效的運行就成為開發中的一個大問題.尤其針對手機等移動資訊裝置時,無線通訊的特點又對我們的程式提出了更高的要求.從代碼最佳化的角度,在網路編程中引入多線程就顯得十分重要。
當程式啟動並執行時候,Application Management Software(應用管理軟體)首先初始化一個MIDlet,然後調用它的startApp()方法使得MIDlet進入active狀態,這條程式分支就是主線程,它執行其他的方法後都會返回到這個分支上來繼續執行。然而網路連接是個可能堵塞的操作,意味著它可能長時間都不返回。
在SUN公司的無線開發包WTK中類比一段網路連接程式運行時,WTK會提示網路連接工作可能會堵塞使用者輸入,需要建立另外一個線程去進行連網操作。針對以上情況,引入多線程的處理機制。
2.1 利用Thread類與Runnable介面
編寫J2ME網路連接應用程式的時候往往藉助Command顯示組件,調用其事件處理函數完成網路的串連工作,代碼架構如下:
public void commandAction(Command c, Displayable s) {
if(c==sendCommand){
requestConnect();//串連方法
}
else if(
c==backCommand){
display.setCurrent(mainForm); }
else{
destroyApp(false);
notifyDestroyed(); }
}
// 擷取一個HTTP的串連
private void requestConnect() {
String url= URL.URLString
HttpConnection hpc = null;
try{
hpc = (HttpConnection)Connector.open(url);
int status = hpc.getResponseCode();
if(status != HttpConnection.HTTP_OK)
content = "聯機失敗!";
else
content = "已聯機!";
}
catch(IOException e){System.out.println(content);}
try{
if(hpc != null) hpc.close();
}
catch(IOException e2){}}
上面的程式工作原理可用圖2的工作原理圖a來表示。
圖2 工作原理圖a
分析圖2可以得出,如果這樣的網路連接程式在手機上運行,那麼將可能長時間得不到響應。因為串連工作只有一個主線程,所有的應用都是在這個主線程當中進行的,如果此主線程不返回,那麼就不能進行後面的行為,使用者也不能進行任何操作。
下面改進一下程式,建立一個實現Runnable介面的ConnectPipe類來建立多線程。代碼如下:
//實現Runnable介面
class ConnectPipe implement Runnable{
……
public void run(){
requestConnect();}
}
修改commandAction函數:
public void commandAction(Command c, Displayable s) {
if(c==sendCommand){
//建立新線程
new Thread(new ConnectPipe()).start();
}
else if(c==backCommand){
……
}
}
修改之後程式能夠較為順利的運行,當處理網路連接的時候,啟動一個線程後主線程會立刻返回,兩個線程並行,不會引發在此地堵塞。其工作原理可用圖3的工作原理圖b來表示。
圖3 工作原理圖b
詳細分析圖3,又發現儘管程式可以正常工作,但是每次使用者按下按鈕的時候都會有新的線程產生,這樣顯然不夠高效,同時,非同步行為又有可能使兩個線程間產生死結。幸好java中提供了wait()和notify()/notifyAll()來進行線程間的通訊,協調同步問題。那麼對應本程式中的線程同步問題,設計思想如下:啟動線程後,讓其進入等待的狀態,當使用者啟用Command事件的時候喚醒線程,才讓其繼續運行。代碼類似如下:
public synchronized void run() {
while (dealing) {
try { wait(); }//線程等待
catch (InterruptedException ie) {}
if (dealing) requestConnect();
}
}
public synchronized void deal() {
notify();//喚醒線程
}
其中dealing變數用於定義一個鎖,當其為true時,當前線程等待,直到使用者啟用Command事件之後,調用deal()方法中的notify()喚醒當前線程繼續運行。這樣程式就顯得相當的高效,也在很大程度上避免了線程間的死結問題。其工作原理可用圖4的工作原理圖c來表示。
圖4 工作原理圖c
2.2 利用系統類別Timer和TimerTask
系統類別Timer類是一個計時器,和TimerTask類結合可以來實現在MIDlet中定時執行特定任務。需要說明的是每一個Timer對象實際上都是一個後台啟動並執行獨立的線程。這是因為調度一次的任務都是由TimerTask類的實現對象負責,TimerTask類是一個抽象類別,它的主要特點是實現了Runnable介面,因此擴充了必須實現的public void run()方法。
所以,在J2ME的網路編程中,我們可以利用Timer類和TimerTask類來建立線程,完成網路連接等工作。設計思想如下:建立一個Timer類計時器,一個完成網路連接功能的TimerTask類,在系統空閑時,反覆調度任務要求串連,直到串連成功,再調用TimerTask類的cancel()可以停止一個具體的調度任務。核心代碼類似如下:
class ConnectTimer implement TimerTask{
ConnectTimer (){
m_Timer = new Timer();//定義Timer
m_Timer.schedule(this,500,5000); //調度任務
}
……
public synchronized void run(){
requestConnect();//串連方法
cancel();//取消任務
}
}
public void commandAction(Command c, Displayable s) {
if(c==sendCommand){
new ConnectTimer;}
else if(c==backCommand){
……
}
}
3、結束語
綜上所述,在J2ME的應用開發中網路程式的設計具有重要的地位,而編程的關鍵又在於編寫高效友好的J2ME網路連接程式。通過Java語言內建的多執行緒機制,利用線程進行同步平行處理,解決了網路連接時的阻塞問題,達到了程式高效啟動並執行目的。