程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2ME >> 全面分析MIDP通信API和外部系統交互

全面分析MIDP通信API和外部系統交互

編輯:J2ME
摘要
  在這個MIDP系列的最後一部分,我將介紹MIDlet和外部系統之間的通信
方法使用Java2 Micro Edition的MIDP中包含的API,開發者可以與外部的系統交互。這篇文章將以一個詳細的例子介紹這些API,它演示了J2ME設備和一個基於servlet的Web系統可以進行的交互。

  在前面的兩個部分,我介紹的MIDP API的特性和
功能都是和設備自身的運作和信息存儲相關的。在這個最後的部分,我將集中介紹網絡設備和大型網絡的交互。

  網絡設備可以使用數不清的協議來進行互相通信。在這篇文章中,我將集中介紹HttpConnection接口,你可以
通過它來訪問存儲在一個Web服務器上的信息。在介紹本文的例子之前,我將討論Javax.microedition.io接口和類的交互,而例子則集中介紹一個MIDP設備和一個基於JSP系統之間的交互。

  Connection層次

  Javax.microedition.io包中的所有接口都是基於Connection接口的。其它的連接接口繼承Connection中的方法,並且定義了用來訪問相關變量和該Connection類型動作的方法。我將在本文中談論最常用的接口,其它的接口留給讀者研究。

  HTTPConnection
說明

  HTTPConnection接口建立在Connection接口上,它還提供了一些其它的方法來進行HTTP交互。以下就是其中一些有用的方法列表:

String getHeaderFIEld(int index)
String getHeaderFIEld(String name)
long getHeaderFIEldDate(String name, long def)
int getHeaderFIEldInt(String name, int def)
String getHeaderFIEldKey(int n)
String getHost()
long getLastModifIEd()
int getPort()
String getProtocol()
String getQuery()
String getRef()
String getRequestMethod()
String getRequestProperty(String key)
int getResponseCode()
String getResponseMessage()
String getURL()
void setRequestMethod(String method)
void setRequestProperty(String key, String value)

  這些方法可讓你如基於servlet的系統一樣訪問HTTP字段。

  其它的接口在API規范文檔中有明確的定義。這些接口帶有一些方法,可以使用不同的協議與設備收發數據包或者流數據。在這裡我將不會很詳細地討論這些,因為在形式上是一樣的。

  Connector對象

  MIDP API
如何知道要創建哪個接口並返回給調用的類呢?答案是返回的Connector是根據傳送給連接字符串的值。

  以下的連接字符串是提醒Connector對象系統正在查找一個HttpConnection:

HttpConnection httpConn = Connector.open("
http://www.itpath.com");

  MIDP Connector對象分析連接的字符串,知道這是一個用作訪問網頁的URL,於是就會返回一個HttpConnection接口的實現給調用的類。

  其它的連接協議需要不同的連接字符串。下表就是它們的一個列表:

協議                               連接字符串
Http                              
http://www.yahoo.com
Stream-based Socket                Socket://localhost:6160
Datagram-based Socket - listening  datagram://:6160  
Datagram-based Socket - sending    datagram://121.232.121.232:6160  
Serial Port                        comm.:0;baudrate=5000
File                               file://helloWorld.txt  

  例子

  以下的例子將本文討論的東西都結合在一起。在這個例子中,MIDlet訪問存儲在一個遠程系統上的信息。該信息以一個XML的形式返回給MIDlet。通過分析該XML,MIDlet就會根據這些數據構造一個用戶界面。

  用戶界面由一個問題構成。用戶提交後將會請求服務器將數據加進去。然後返回更新的數據給用戶。

通過這個詳細的例子,你可以對J2ME Connection API的基本用法和語法有更進一步的了解。

  VoterMidlet

  VoterMidlet是本例子中唯一的MIDlet。在下載時,它創建VoteResults對象的一個實例:

public class VoterMidlet extends MIDlet implements ScreenCallback
{;
 private Display _display;
 // midlet has three screens
 private VoteResults voteResults = new VoteResults(
   (ScreenCallback) this);

 public VoterMidlet()
 {;
  _display = Display.getDisplay(this);
  _display.setCurrent(voteResults);
 };

 public void exit()
 {;
  try
  {;
   this.destroyApp(true);
  }; catch (MIDletStateChangeException e)
  {;};
 };
 ...
};

  ScreenCallback

  如上所示,VoterMidlet實現了ScreenCallback接口。該接口將UI類和一些事件隱藏起來,否則UI類可能需要一個到MIDlet的引用。ScreenCallback接口包含有一個單一的方法public void exit(),UI屏幕使用這個方法來提醒MIDlet,用戶已經按下“Exit”按鈕。

  通過為ScreenCallback寫代碼,其它的MIDlet方法就與UI屏幕的開發者無關。這是很重要的,因為如果使用不當,一些MIDlet方法會給程序帶來破壞性的後果。

  VoteResults

  VoteResults是一個用戶接口類,用來顯示投票的結果給用戶。為了讓例子簡化,該接口實現了兩個Model-View-Controller類:VIEw和Controller。

  該構造器由MIDlet中接收唯一一個參數--ScreenCallback接口。如上所述,該接口可讓Screen能夠回調MIDlet的一些方法。

  構造器初始化該對象並且創建例子的用戶界面,以下是其中令人感興趣的部分:

VoteSummary voteSummary = ResourceUtility.getVoteSummary();
initialize(voteSummary);  

   以上的代碼是負責初始化MIDlet和JSP頁面的通信,它模擬的是一個真實的系統。ResourceUtility通過HTTP參數訪問一個URL,並且由該JSP中得到信息。它使用該信息來創建一個VoteSummary對象。在後面的例子中我們還將進一步討論這個接口。

  然後就會調用initialize()方法來創建UI顯示,它包括有兩個StringItems顯示前面的投票結果,還有一個ChoiceGroup包含有所有可能的投票和相應的投票值:

public void initialize(VoteSummary voteSummary)
{;
 append( getNumVotesString( voteSummary.getNumVotes() )) ;
 append( getAvgVoteString( voteSummary.getAvgVote() ));
 append( showVoteResults( voteSummary.getVotes() ));
};


  當用戶輸入時,就會調用commandAction()方法。該方法接收設備的輸入。如果輸入的命令是“Vote”,當前的選項就會由ChoiceGroup中得到。這時就會構造一個Vote對象,並且傳送給ResourceUtility.addEntry()方法。該方法就會將信息傳送給JSP,JSP就會將新投票加入到記錄中,並且返回一個更新的VoteSummary對象。然後它就會調用update()方法:

public void commandAction(Command c, Displayable s)
{;
 String command = c.getLabel();

 if ( command.equals("Exit") )
 {;
  _screenCallback.exit();
 };
 else if ( command.equals("Vote") )
 {;
  // get the selected item
  int selectedIndex = _voteResults.getSelectedIndex();
 
  Vote newVote = new Vote(""+ selectedIndex, null, null);

  VoteSummary voteSummary = ResourceUtility.addEntry( newVote);

  update( voteSummary );
 };
};

  VoteSummary

  VoteSummary對象包含有當前投票的狀態信息。它跟蹤全部的投票數字,平均的投票,以及維護投票的一個Vector(可得到每個選項的投票信息):

public VoteSummary(String numVotes, String avgVote, Vector votes)
{;
_numVotes = numVotes;
_avgVote = avgVote;
_votes = votes;
};

  根據由JSP返回到MIDlet的XML,就會創建VoteSummary對象。

  ResourceUtility

  ResourceUtility類由遠程服務器上得到XML信息。它使用HttpConnection由JSP中得到XML,然後就會通過XMLUtil 將這些XML轉換為一個VoteSummary 對象:

public static VoteSummary getVoteSummary()
{;
 String XML = loadVoteResults();

 return convertXMLToVoteSummary( XML);
};

  上面的getVoteSummary()方法訪問這個對象。該方法會依次調用loadVoteResults()和convertXMLToVoteSummary()方法。loadVoteResults()方法如下所示,它調用了backendComms()方法,該方法用來為後台系統增加投票:

public static String loadVoteResults()
{;
 return backendComms(LOAD_URL, "");
};
private static String backendComms(String requestURL, String requeststring)
{;
 HttpConnection httpConnection = null;
 DataInputStream dataInputStream = null;
 StringBuffer messagebuffer = new StringBuffer();

 String requestString = requestURL + requeststring;

 Try
 {;
  // Open an HTTP connection with the Web server
  httpConnection = (HttpConnection)
  Connector.open(requestString, Connector.READ_WRITE);

  // Set the request method to GET
  httpConnection.setRequestMethod(HttpConnection.GET);

  // RetrIEve the response back from the servlet
  dataInputStream =
   new DataInputStream(httpConnection.openInputStream());

  int inputChar;

  // Check the Content-Length first
  long contentLength = httpConnection.getLength();

  if(contentLength!=-1)
  {;
   for(int i = 0;I    {;
    if((inputChar = dataInputStream.read())!= -1)
    {;
     messagebuffer.append((char)inputChar);
    };
   };
   }; else {;
    // if the content-length is not available
    while ((inputChar = dataInputStream.read()) != -1)
    {;
     messagebuffer.append((char) inputChar);
     };
    };
   dataInputStream.close();

  }; catch (IOException ioe) {;
   messagebuffer = new StringBuffer("ERROR!");
  }; catch (Exception e){;
    e.printStackTrace();
  }; finally {;
  try {;
   if (httpConnection != null){
     httpConnection.close();
   }; catch (IOException ignored) {;};
   try {;
    if (dataInputStream != null)
     dataInputStream.close();
    }; catch (IOException ignored) {;};
   };
   return messagebuffer.toString();
  };

  以下我們討論一下backendComms()方法。首先由try-catch塊開始。第一步是打開一個到服務器的讀/寫HttpConnection。上面我已經說過,該方法得到XML並且對服務器發出寫請求,因此我們需要使用一個READ_WRITE連接:

try
{;
 // Open an HTTP connection with the Web server
 httpConnection = (HttpConnection)
 Connector.open(requestString, Connector.READ_WRITE);

 // Set the request method to GET
 httpConnection.setRequestMethod(HttpConnection.GET);

 // RetrIEve the response back from the JSP
 dataInputStream =
  new DataInputStream(httpConnection.openInputStream());


  要得到一個HttpConnection實現,我們調用Connector.open()方法。該類包含有靜態的方法,可根據連接字符串產生用作通信的相應接口。在這個例子中,我們要得到一個HttpConnection,因此我們將Connector.open()的響應放入HttpConnection接口。

  一旦我們發出請求,我們就可以使用HttpConnection對象的openInputStream()方法,通過它可由JSP中得到結果的InputStream:

if(contentLength!=-1)
{;
 for(int i = 0;I  {;
  if((inputChar = dataInputStream.read())!= -1)
  {;
   messagebuffer.append((char)inputChar);
  };
 };
}; else {;
 // if the content-length is not available
 while ((inputChar = dataInputStream.read()) != -1)
 {;
  messagebuffer.append((char) inputChar);
 };
};

  然後就可以讀取該響應內容的長度,如果沒有找到,你也可以一直讀取它直到得到一個EOF或者出現一個錯誤的字符。每個由DataInputStream讀取的字符都被加入到StringBuffer中。這個StringBuffer包含有一個XML表示的投票結果,它將被傳送回調用的方法,然後再傳送到convertXMLToVoteSummary()方法,如下所示:

private static VoteSummary convertXMLToVoteSummary(String XML)
{;
 InputStreamReader insr = new InputStreamReader(
 new ByteArrayInputStream(XML.getBytes() ) );

 VoteSummary voteSummary = null;

 Try
 {;
  voteSummary = XMLUtil.getVoteResults(insr);

 }; catch (IOException ioe)
  {;};

 return voteSummary;
};

  該方法將XML字符串轉換為一個Java對象,這樣我們就可以使用方法來得到數據,而不是分析數據。我們使用XMLUtil.getVoteResults()方法來做這個轉換。在接觸該方法前,我們先完成ResourceUtility對象。最後的一個方法是addEntry(),將新的投票傳送到JSP。如上所述,為了效率,它再次使用backendComms()方法:

public static VoteSummary addEntry(Vote vote)
{;
 StringBuffer requestString = new StringBuffer();

 requestString.append(QUESTION + vote.toRequestString() );

 String XML = backendComms(LOAD_URL, requestString.toString() );

 return convertXMLToVoteSummary(XML);
};

  XMLUtil

  XMLUtil對象的作用是將由JSP接收的XML轉換為一個VoteSummary對象,以便應用使用。要完成轉換,它必須使用一個XML分析器來解析數據。為了減少MIDlet的整體大小,所以要使用一個輕量級的分析器以令MIDlet的整體大小最小化。在這個例子中,我使用的是Enhydra的kXML分析器。

  以下就是這個對象中的getVoteResults()方法,我們來看看轉換過程是如何進行的:

public static VoteSummary getVoteResults(InputStreamReader insr)
throws IOException
{;
 XmlParser parser = new XMLParser (insr);

 Document document = new Document();
 document.parse( parser );

 file://uncomment to see the document written to your console.
 file://document.write( new XMLWriter( new OutputStreamWriter
                  ( System.out) ) );

  前幾行是設置XmlParser和Document對象的。在創建這些對象後,就會調用Document.parse()方法,並且傳送XMLParser作為參數。結果的文檔包含有一個由JSP傳送來的XML信息。如果你對驗證信息的格式感興趣,你可以將ocument.write調用前面的注釋去掉,以看請文檔的結構:

Element voteExampleElement = document.getElement("vote-example");
Element voteElement = voteExampleElement.getElement("votes");

String numVotes = getTextFromElement(voteElement, "number");
String avgVote = getTextFromElement(voteElement, "average");

  在創建Document後,我們現在可使用DOM方法來得到Element對象並且獲取其中的值。頭兩行是用來在XML文檔中移動,以到達含有數據的節點。在得到voteElement節點後,我們就可以使用getTextFromElement()方法,這個方法執行多個命令從文檔中得到文本節點,並且返回它的值:

Element choicesElement = voteElement.getElement("choices");
int childCount = choicesElement.getChildCount();

  上面的兩行代碼用來取得在選擇區域下的節點數目。使用childCount,你可以構造節點,這樣就無需使用當前的投票數目。這樣做是很重要的,因為以後你可能需要加入更多的投票。使用這些調用,你可以無需修改代碼就做到這一點:

for (int i = 0; i < childCount; i++)
{;
 if ( choicesElement.getType(i) == XML.ELEMENT)
 {;
  choiceElement = choicesElement.getElement(i);

  choicevalue = getTextFromElement(choiceElement,"value");
  choiceName = getTextFromElement(choiceElement,"name");
  choiceVotes = getTextFromElement(choiceElement,"number");

  vote = new Vote(choicevalue, choiceName, choiceVotes);

  vEntrIEs.addElement(vote);
  };
 };

 return new VoteSummary( numVotes, avgVote, vEntrIEs);

  這樣我們就可以得到子元素的數目,然後可以遍歷它們並且取得其中的投票信息。其中一些子節點是Element,而其它的是沒有數據的Text節點。在這個例子中,我們僅關心Elements,因此我們要在循環中設置相應的條件。該循環將由選項Element中得到每個子Elements,並且構造一個Vote對象,該對象包含有值(用作計算)、名字和投票的數目。這些vote對象都被依次加入到Vector中。最後,這個方法初始化一個新的VoteSummary對象,該對象包含有以上三個信息。

private static String getTextFromElement(Element elementRoot,
String elementName)
{;
 String returnText = elementRoot.getElement(elementName).getText();

 return returnText;
};

  我們使用getTextFromElement()方法來令代碼的可讀性更強。它的作用是深入樹形結構一層,並且得到其中的Text節點信息。

  voter.JSP

  這個系統的最後一個元素是模擬後端系統的JSP。在這個例子中,我創建了一個簡單的JSP,它使用類變量來跟蹤投票計算。

  具體的JSP見源代碼。它只是跟蹤投票並且返回以XML表示的數據。

  最後的要點

  這個例子講解了MIDP和一個服務器端的組件如果通過HttpConnection來進行交互。通過發出一個請求,MIDP可以通過普通的HTTP GET/POST調用來與JSP組件通信。在這個例子中,調用返回XML信息。

  使用kXML分析器,XML可以被有效地轉換為一個對象,圖形類就可以由該對象中獲取數據。你也要注意到,在應用中的ChoiceGroup中並沒有定義選項的數目,而是通過服務器端的組件動態設置。

  與任何多層開發任務一樣,要考慮如何有效地實現Model-VIEw-Controller模型而避免創建多余的代碼,這個想法在這個例子中也有體現,這個模型就是JSP。vIEw就是顯示全部數據信息的VoteResults對象。在這裡的controller也是VoteResults對象,由於它實現了CommandListener接口。

  通信

  在這篇文章中,我們主要討論了一個可產生HTTP請求的MIDP通信系統。這些請求可以是由使用標准協議的已知端口到任何使用私有協議的應用級協議。其中的API都可以很靈活地以一個很類似的方式處理這些請求。

  在與遠程機器交互的時候,如果你處於一個覆蓋以外的區域(例如進入一個隧道時),這時會怎樣呢?與WAP不同,在處於覆蓋以外的區域時,具有MIDP功能的電話仍然可以繼續運行它的應用。這個技術可以做到讓用戶使用離線的功能,並且在回到覆蓋區域時,具有與在線應用同步的能力。例如一個基於MIDP的email應用:用戶可以繼續讀取和回應已經下載到本地RMS datastore的email。在回到覆蓋區域時,回復的信息可以發送到服務器,從而分發到不同的接收者。

  當前發布的J2ME Wireless Toolkit版本並不支持HTTPS協議。不過,早些發布的開發者版本確實支持HTTPS。通過這些API的支持,你可以實現在Java電話上交易股票或者進行買賣。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved