Android使用XML全攻略(2)
Android 是針對移動裝置的一種新興的開源作業系統和 SDK。借助它,您可以建立功能強大的移動應用程式。當您的應用程式可以通路 Web 服務時,其吸引力會大大增加,這意味着您需要使用 Web 語言:XML。在本文中,您将了解在 Android 上使用 XML 的不同方法,以及如何使用它們建構自己的 Android 應用程式。
更加簡單的 SAX 解析
Android SDK 提供了一個名稱為android.util.Xml的實用類。清單 7 展示了如何使用這個相同的實用類來設定一個 SAX 解析器。
清單 7. Android SAX 解析器
- public class AndroidSaxFeedParser extends BaseFeedParser {
- public AndroidSaxFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- RssHandler handler = new RssHandler();
- try {
- Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, handler);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return handler.getMessages();
- }
- }
注意,這個類仍然使用了一個标準的 SAX 處理程式,是以您僅僅重用了 清單 7 中所示的 RssHandler。能夠重用 SAX 處理程式是非常不錯的,但其代碼稍微有些複雜。您可以想像,如果需要解析一個更加複雜的 XML 文檔,則處理程式可能會帶來各種各樣的 bug。舉例來說,回頭看看 清單 6 中的 endElement 方法。注意,在嘗試設定屬性之前,它檢查了 currentMessage 是否為 null。現在,再回頭看看 清單 4 中的示例 XML。 注意,ITEM 标記外部有一些 TITLE 和 LINK 标記。這就是使用 null 檢查的原因。否則,每一個 TITLE标記 會導緻一個 NullPointerException。Android 提供了自己獨有的 SAX API(參見 清單 8),它排除了您編寫自己的 SAX 處理程式的需要。
清單 8. 經過簡化的 Android SAX 解析器
- public class AndroidSaxFeedParser extends BaseFeedParser {
- public AndroidSaxFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- final Message currentMessage = new Message();
- RootElement root = new RootElement("rss");
- final List<Message> messages = new ArrayList<Message>();
- Element channel = root.getChild("channel");
- Element item = channel.getChild(ITEM);
- item.setEndElementListener(new EndElementListener(){
- public void end() {
- messages.add(currentMessage.copy());
- }
- });
- item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setTitle(body);
- }
- });
- item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setLink(body);
- }
- });
- item.getChild(DESCRIPTION).setEndTextElementListener(new
- EndTextElementListener(){
- public void end(String body) {
- currentMessage.setDescription(body);
- }
- });
- item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
- public void end(String body) {
- currentMessage.setDate(body);
- }
- });
- try {
- Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8,
- root.getContentHandler());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
新的SAX 解析代碼并未使用 SAX 處理程式,而是使用了 SDK 中的 android.sax 包中的類。這些類允許您建構 XML 文檔的結構,并根據需要添加事件監聽程式。在以上代碼中,您聲明文檔将有一個 rss 根元素,并且它有一個 channel 子元素。然後,您聲明channel 将有一個 ITEM 子元素,并且開始添加監聽程式。對于每個監聽程式,您都使用了一個實作了特定接口(EndElementListner或 EndTextElementListener)的匿名内部類。注意,您不需要跟蹤字元資料。不僅僅因為這樣會更加簡單,更重要的是更加高效。最後,在調 用 Xml.parse 實用方法時,您将傳遞一個通過根元素生成的處理程式。
清單 8 中的所有代碼都是可選的。如果您習慣 Java 環境中的标準 SAX 解析代碼,那麼您可以堅持使用它。如果您希望嘗試 Android SDK 所提供的便捷的包裝器,那麼也可以使用它。如果您完全不希望使用 SAX 會怎樣呢?可以使用一些備選方案。其中的首選方法就是 DOM。
使用 DOM
Android 完全支援 DOM 解析,就像在桌面機器或伺服器上使用 Java 代碼運作它一樣。清單 9 顯示了一個基于 DOM 的解析器接口實作。
清單 9. 基于 DOM 的提要解析器實作
- public class DomFeedParser extends BaseFeedParser {
- protected DomFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- List<Message> messages = new ArrayList<Message>();
- try {
- DocumentBuilder builder = factory.newDocumentBuilder();
- Document dom = builder.parse(this.getInputStream());
- Element root = dom.getDocumentElement();
- NodeList items = root.getElementsByTagName(ITEM);
- for (int i=0;i<items.getLength();i++){
- Message message = new Message();
- Node item = items.item(i);
- NodeList properties = item.getChildNodes();
- for (int j=0;j<properties.getLength();j++){
- Node property = properties.item(j);
- String name = property.getNodeName();
- if (name.equalsIgnoreCase(TITLE)){
- message.setTitle(property.getFirstChild().getNodeValue());
- } else if (name.equalsIgnoreCase(LINK)){
- message.setLink(property.getFirstChild().getNodeValue());
- } else if (name.equalsIgnoreCase(DESCRIPTION)){
- StringBuilder text = new StringBuilder();
- NodeList chars = property.getChildNodes();
- for (int k=0;k<chars.getLength();k++){
- text.append(chars.item(k).getNodeValue());
- }
- message.setDescription(text.toString());
- } else if (name.equalsIgnoreCase(PUB_DATE)){
- message.setDate(property.getFirstChild().getNodeValue());
- }
- }
- messages.add(message);
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
與第一個 SAX 示例類似,以上代碼完全沒有特定于 Android 的地方。DOM 解析器将所有 XML 文檔讀取到記憶體中,然後允許您使用 DOM API 周遊 XML 樹、檢索所需的資料。這是非常直覺的代碼,并且,在某些方面比基于 SAX 的實作更加簡單。但是,DOM 通常更加占用記憶體,因為一切内容都會先讀取到記憶體中。這對于運作 Android 的移動裝置來說是一個問題,但是當 XML 文檔始終保持很小的大小時是可行的。這可能意味着,Android 的開發人員會認為 SAX 解析在 Android 應用程式上更加常見,是以為它提供了額外的實用工具。Android 還提供了另一種類型的 XML 解析器,它就是 pull 解析器。
XML pull 解析器
如前所述,Android 并未提供對 Java StAX API 的支援。但是,Android 确實附帶了一個 pull 解析器,其工作方式類似于 StAX。它允許您的應用程式代碼從解析器中擷取事件,這與 SAX 解析器自動将事件推入處理程式相反。清單 10 顯示了提要解析接口的一個 pull 解析器實作。
清單 10. 基于 Pull 解析器的實作
- public class XmlPullFeedParser extends BaseFeedParser {
- public XmlPullFeedParser(String feedUrl) {
- super(feedUrl);
- }
- public List<Message> parse() {
- List<Message> messages = null;
- XmlPullParser parser = Xml.newPullParser();
- try {
- // auto-detect the encoding from the stream
- parser.setInput(this.getInputStream(), null);
- int eventType = parser.getEventType();
- Message currentMessage = null;
- boolean done = false;
- while (eventType != XmlPullParser.END_DOCUMENT && !done){
- String name = null;
- switch (eventType){
- case XmlPullParser.START_DOCUMENT:
- messages = new ArrayList<Message>();
- break;
- case XmlPullParser.START_TAG:
- name = parser.getName();
- if (name.equalsIgnoreCase(ITEM)){
- currentMessage = new Message();
- } else if (currentMessage != null){
- if (name.equalsIgnoreCase(LINK)){
- currentMessage.setLink(parser.nextText());
- } else if (name.equalsIgnoreCase(DESCRIPTION)){
- currentMessage.setDescription(parser.nextText());
- } else if (name.equalsIgnoreCase(PUB_DATE)){
- currentMessage.setDate(parser.nextText());
- } else if (name.equalsIgnoreCase(TITLE)){
- currentMessage.setTitle(parser.nextText());
- }
- }
- break;
- case XmlPullParser.END_TAG:
- name = parser.getName();
- if (name.equalsIgnoreCase(ITEM) &&
- currentMessage != null){
- messages.add(currentMessage);
- } else if (name.equalsIgnoreCase(CHANNEL)){
- done = true;
- }
- break;
- }
- eventType = parser.next();
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return messages;
- }
- }
pull 解析器的運作方式與 SAX 解析器相似。它提供了類似的事件(開始元素和結束元素),但您需要使用 (parser.next() 提取它們。事件将作為數值代碼被發送,是以您可以使用一個簡單 case-switch。注意,解析并未像 SAX 解析那樣監聽元素的結束,而是在開始處完成了大部分處理。在 清單 10 的代碼中,當某個元素開始時,您可以調用 parser.nextText() 從 XML 文檔中提取所有字元資料。還需注意,您設定了一個标記(布爾變量 done)來确定何時到達感興趣内容的結束部分。這允許您提早停止讀取 XML 文檔,因為您知道代碼将不會關心文檔的其餘部分。這有時非常實用,特别是當您隻需要通路一小部分 XML 文檔時。通過盡快停止解析,您可以極大地減少解析時間。這種優化對于連接配接速度較慢的移動裝置尤為重要。pull 解析器可以提供一些性能優勢以及易用性。它還可以用于編寫 XML。
建立 XML
目前為止,我一直專注于通過 Internet 解析 XML。但是,有時您的應用程式可能需要将 XML 發送到遠端伺服器。顯然,您可以隻使用一個 StringBuilder 來建立 XML 字元串。另一種備選方法來自 清單 11 中的 Pull 解析器。
清單 11. 使用 pull 解析器編寫 XML
- private String writeXml(List<Message> messages){
- XmlSerializer serializer = Xml.newSerializer();
- StringWriter writer = new StringWriter();
- try {
- serializer.setOutput(writer);
- serializer.startDocument("UTF-8", true);
- serializer.startTag("", "messages");
- serializer.attribute("", "number", String.valueOf(messages.size()));
- for (Message msg: messages){
- serializer.startTag("", "message");
- serializer.attribute("", "date", msg.getDate());
- serializer.startTag("", "title");
- serializer.text(msg.getTitle());
- serializer.endTag("", "title");
- serializer.startTag("", "url");
- serializer.text(msg.getLink().toExternalForm());
- serializer.endTag("", "url");
- serializer.startTag("", "body");
- serializer.text(msg.getDescription());
- serializer.endTag("", "body");
- serializer.endTag("", "message");
- }
- serializer.endTag("", "messages");
- serializer.endDocument();
- return writer.toString();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
XmlSerializer 類是 前一部分 所使用的 XmlPullParser 包的一部分。它沒有提取事件,而是将它們推出到資料流或編寫程式中。在本例中,它僅僅将事件推送到了一個 java.io.StringWriter 執行個體中。它提供了一個直覺的 API,通過各種方法開始和結束文檔、處理元素以及添加文本或屬性。這是StringBuilder的一種出色的替換方案,因為它可以更加輕松地確定您的 XML 具有良好結構。
結束語
您希望為Android裝置建構何種類型的應用程式?無論如何,如果它需要通過 Internet 使用資料,那麼都可能需要使用XML。在本文中,您看到 Android 提供了大量用于處理XML的工具。您可以選擇其中之一作為自己的工具,或者您可以根據用例來進行選擇。大多數時間,使用SAX是比較安全的,并且Android提供了一種傳統的SAX 使用方法,以及一個便捷的SAX包裝器。如果您的文檔比較小,那麼DOM可能是一種比較簡單的方法。如果您的文檔比較大,但您隻需要文檔的一部分,則XML Pull解析器可能是更為有效的方法。最後,對于編寫XML,Pull解析器包也提供了一種便捷的方法。是以,無論您的XML需求如何,Android都能在一定程度上滿足它們。
轉載于:https://www.cnblogs.com/wuwa/p/6191572.html