Android で XML を扱う

モバイル機器用の Java アプリケーションを作成する

Android は、モバイル機器を対象としたオープンソースのオペレーティング・システムとして最新のものであり、同時に SDK でもあります。Android を利用すると、強力なモバイル・アプリケーションを作成することができます。そのアプリケーションが Web サービスにアクセスできる場合には一層魅力的なアプリケーションになります。その場合、Web の言語である XML を使用する必要があります。この記事では、Android で XML を扱うためのさまざまな方法について、またそれらの方法を使って独自の Android アプリケーションを作成する方法について説明します。

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin は eBay のアーキテクトです。彼は developerWorks に頻繁に寄稿しており、また TheServerSide.com や Java Developer's Journal、そして彼自身のブログにも記事を執筆しています。彼は 1998年からプロとしてプログラミングを行っており、California Institute of Technology で数学の学位を取得しています。



2009年 6月 23日

はじめに

この記事では、インターネット上の XML データを扱う Android アプリケーションの作成方法について学びます。これらの Android アプリケーションは Java™ プログラミング言語で作成されるため、Java 技術の経験が必須となります。Android アプリケーションの開発を行うには Android SDK が必要です。この記事で説明するコードは、どのバージョンの Android SDK でも動作しますが、この記事のコードの作成には SDK 1.5_pre を使用しました。Android アプリケーションは SDK とテキスト・エディターさえあれば作成できますが、Eclipse プラグインである ADT (Android Developer Tools) を使った方が、はるかに簡単です。この記事では、Eclipse 3.4.2 の Java 版で ADT のバージョン 0.9 を使用しました。これらのツールへのリンクは「参考文献」を参照してください。


Android での XML

Android プラットフォームはオープンソースのモバイル・ソフトウェア開発用のプラットフォームです。このプラットフォームを使うことによって、低レベルのグラフィックスから携帯電話のカメラのようなハードウェアに至るまで、Android が稼働するモバイル機器のあらゆる側面にアクセスすることができます。それほど多くのことが Android によって可能になるのであれば、なぜ XML にこだわる必要があるのか、不思議に思う人がいるかもしれません。XML を扱うことが興味深いのではなく、XML によって可能になることを扱うことが興味深いのです。XML はインターネット上のデータ・フォーマットとして一般的に使われています。インターネット上のデータにアクセスしたい場合、そのデータの形式はおそらく XML です。Web サービスにデータを送信したい場合も、XML を送信する必要があります。要するに、Android アプリケーションでインターネットを活用しようとするのであれば、おそらく XML を扱う必要があるのです。幸いなことに、Android で XML を扱う方法は豊富にあります。


XML パーサー

頻繁に使用する頭字語

  • API: Application Programming Interface
  • RSS: Really Simple Syndication
  • SDK: Software Developers Kit
  • UI: User Interface
  • URL: Universal Resource Locator
  • XML: Extensible Markup Language

Android プラットフォームの最大の強みの 1 つは、Java プログラミング言語を利用していることです。標準的な JRE (Java Runtime Environment) で利用できるもののすべてを Android SDK が提供しているわけではありませんが、JRE の大部分がサポートされています。Java プラットフォームは XML を扱うためのさまざまな方法を長年サポートしてきており、XML に関連する Java API の大部分は Android でも完全にサポートされています。例えば、Java の SAX (Simple API for XML) と DOM (Document Object Model) は、長年にわたって Java 技術の一部として使われている API ですが、どちらも Android でも使用することができます。一方、新しい StAX (Streaming API for XML) は Android では使用することができませんが、代わりに Android には StAX と機能的に等価なライブラリーが用意されています。また、Android には Java XML Binding API がありません。この API も Android に実装できたはずです。しかしこの API は重たくなりがちであり、1 つの XML 文書を表現するためにさまざまなクラスのインスタンスが数多く必要になることが多いものです。従って、Android が実行されるハンドヘルド機器のように制約のある環境には、Java XML Binding API はあまり適していません。以下のセクションでは、インターネット上で入手できる単純な XML ソースを例に、Android アプリケーションの中で上述のさまざまな API を使ってこの XML ソースを構文解析する方法を説明します。最初に、インターネット上の XML を使用する簡単なアプリケーションの肝となる部分を調べます。


Android ニュース・リーダー

このアプリケーションは、Android 開発者のサイトとして人気のある Androidster から RSS フィードを取得し、単純な Java オブジェクトの一覧へと構文解析し、この一覧を Android の ListView で利用できるにします (ソース・コードは「ダウンロード」セクションを参照してください)。これは典型的なポリモーフィックな動作です。つまり異なる実装 (異なる XML 構文解析アルゴリズム) によって同じ動作を実現することができます。インターフェースを使うことで、この動作を Java コードで容易にモデリングできることを示したものがリスト 1 です。

リスト 1. XML フィード・パーサーのインターフェース
package org.developerworks.android;
import java.util.List;

public interface FeedParser {
    List<Message> parse();
}

リスト 2Message クラスは、データ構造を表す典型的な POJO (Plain Old Java Object) です。

リスト 2. Message という POJO
public class Message implements Comparable<Message>{
    static SimpleDateFormat FORMATTER = 
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
    private String title;
    private URL link;
    private String description;
    private Date date;

      // getters and setters omitted for brevity
    public void setLink(String link) {
        try {
            this.link = new URL(link);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    public String getDate() {
        return FORMATTER.format(this.date);
    }

    public void setDate(String date) {
        // pad the date if necessary
        while (!date.endsWith("00")){
            date += "0";
        }
        try {
            this.date = FORMATTER.parse(date.trim());
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
    
    @Override
    public String toString() {
             // omitted for brevity
    }

    @Override
    public int hashCode() {
            // omitted for brevity
    }
    
    @Override
    public boolean equals(Object obj) {
            // omitted for brevity
    }
      // sort by date
    public int compareTo(Message another) {
        if (another == null) return 1;
        // sort descending, most recent first
        return another.date.compareTo(date);
    }
}

リスト 2 の Message クラスの大部分は単純です。Message では、単純なストリングとして日付とリンクにアクセスできるようにすることで内部状態の一部を隠す一方、強く型付けされたオブジェクトとして日付とリンクを表現します (java.util.Datejava.net.URL)。Message は典型的な値オブジェクトなので、その内部状態に基づいて equals()hashCode() を実装します。また Message は Comparable インターフェースも実装するため、このインターフェースを使って (日付で) ソートすることもできます。実際には、データは必ずソートされた状態でフィードから提供されるため、ソートの必要はありません。

それぞれのパーサー実装は、Androidster フィードの URL を使って Androidster サイトへの HTTP 接続を開く必要があります。この共通動作は、抽象基底クラスを使って自然な形で Java コードの中にモデリングすることができます (リスト 3)。

リスト 3. フィード・パーサーの基底クラス
public abstract class BaseFeedParser implements FeedParser {

    // names of the XML tags
    static final String PUB_DATE = "pubDate";
    static final  String DESCRIPTION = "description";
    static final  String LINK = "link";
    static final  String TITLE = "title";
    static final  String ITEM = "item";
    
    final URL feedUrl;

    protected BaseFeedParser(String feedUrl){
        try {
            this.feedUrl = new URL(feedUrl);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    protected InputStream getInputStream() {
        try {
            return feedUrl.openConnection().getInputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

この基底クラスは feedUrl を保存し、この feedUrl を使って java.io.InputStream を開きます。何か問題がある場合には単純に RuntimeException がスローされるため、アプリケーションは即座に停止します。またこの基底クラスは、タグの名前用に単純な定数もいくつか定義します。リスト 4 は、このフィードの内容の一例です。これを見ると、こうしたタグの重要さを理解できるはずです。

リスト 4. XML フィードの例
<?xml version="1.0" encoding="UTF-8"?>

<!-- generator="FeedCreator 1.7.2" -->

<rss version="2.0">
    
  <channel>
        
    <title>android_news</title>
           
    <description>android_news</description>
        
    <link>http://www.androidster.com/android_news.php</link>
        
    <lastBuildDate>Sun, 19 Apr 2009 19:43:45 +0100</lastBuildDate>
        
    <generator>FeedCreator 1.7.2</generator>
        
    <item>
            
      <title>Samsung S8000 to Run Android, Play DivX, Take Over the 
World</title>
             
      <link>http://www.androidster.com/android_news/samsung-s8000-to-run-android-
play-divx-take-over-the-world</link>
            
      <description>More details have emerged on the first Samsung handset 
to run Android. A yet-to-be announced phone called the S8000 is being 
reported ...</description>
            
      <pubDate>Thu, 16 Apr 2009 07:18:51 +0100</pubDate>
        
    </item>
        
    <item>
            
      <title>Android Cupcake Update on the Horizon</title>
            
      <link>http://www.androidster.com/android_news/android-cupcake-update-
on-the-horizon</link>
            
      <description>After months of discovery and hearsay, the Android 
build that we have all been waiting for is about to finally make it 
out ...</description>
            
      <pubDate>Tue, 14 Apr 2009 04:13:21 +0100</pubDate>
        
    </item>
    
  </channel>

</rss>

リスト 4 のサンプルを見るとわかるように、ITEMMessage インスタンスに対応します。item の子ノード (TITLELINK など) は Message インスタンスのプロパティーに対応します。これでフィードがどのようなものかを理解でき、共通部分をすべて用意できたので、Android に容易されたさまざまな技術を使ってこのフィードを構文解析する方法を調べてみましょう。まず SAX から始めます。


SAX を使う

Java 環境では、高速のパーサーが必要な場合、またアプリケーションのメモリーのフットプリントを最小限にとどめたい場合、SAX API がよく使われます。この点から、SAX は Android を実行するモバイル機器に非常に適しています。SAX API は、Java 環境からそのまま使用することができ、Android 上で実行するために特別な変更をする必要はありません。リスト 5FeedParser インターフェースに SAX を実装した例を示しています。

リスト 5. SAX の実装
public class SaxFeedParser extends BaseFeedParser {

    protected SaxFeedParser(String feedUrl){
        super(feedUrl);
    }
    
    public List<Message> parse() {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            SAXParser parser = factory.newSAXParser();
            RssHandler handler = new RssHandler();
            parser.parse(this.getInputStream(), handler);
            return handler.getMessages();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } 
    }
}

SAX を使ったことがある人には、この実装はとても馴染みがあるはずです。どのような SAX 実装の場合も同じですが、詳細の大部分は SAX ハンドラーの中にあります。SAX ハンドラーは XML 文書の中を進みながら、SAX パーサーからのイベントを受信します。この場合には、RssHandler という新しいクラスを作成し、このクラスをパーサーのハンドラーとして登録しています (リスト 6)。

リスト 6. SAX ハンドラー
import static org.developerworks.android.BaseFeedParser.*;

public class RssHandler extends DefaultHandler{
    private List<Message> messages;
    private Message currentMessage;
    private StringBuilder builder;
    
    public List<Message> getMessages(){
        return this.messages;
    }
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        super.characters(ch, start, length);
        builder.append(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String name)
            throws SAXException {
        super.endElement(uri, localName, name);
        if (this.currentMessage != null){
            if (localName.equalsIgnoreCase(TITLE)){
                currentMessage.setTitle(builder.toString());
            } else if (localName.equalsIgnoreCase(LINK)){
                currentMessage.setLink(builder.toString());
            } else if (localName.equalsIgnoreCase(DESCRIPTION)){
                currentMessage.setDescription(builder.toString());
            } else if (localName.equalsIgnoreCase(PUB_DATE)){
                currentMessage.setDate(builder.toString());
            } else if (localName.equalsIgnoreCase(ITEM)){
                messages.add(currentMessage);
            }
            builder.setLength(0);    
        }
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        messages = new ArrayList<Message>();
        builder = new StringBuilder();
    }

    @Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes) throws SAXException {
        super.startElement(uri, localName, name, attributes);
        if (localName.equalsIgnoreCase(ITEM)){
            this.currentMessage = new Message();
        }
    }
}

org.xml.sax.helpers.DefaultHandler クラスを継承する RssHandler クラスは、SAX パーサーによって発生するイベントに対応するすべてのメソッドに対して、デフォルトの「何もしない」実装を提供します。こうすることによって、サブクラスは必要に応じてメソッドをオーバーライドすればよいことになります。RssHandler には、getMessages という、もう 1 つの API があります。この API は、RssHandler が SAX パーサーからのイベントを受信しながら収集する、Message オブジェクトの一覧を返します。RssHandler には、他にも 2 つの内部変数があります (構文解析対象の Message インスタンスに対する currentMessage と、テキスト・ノードからの文字データを保存する、builder という StringBuilder 変数の 2 つ)。この 2 つの内部変数はどちらも、対応するイベントをパーサーがハンドラーに送信して startDocument メソッドが呼び出されると初期化されます。

リスト 6startElement メソッドを見てください。このメソッドは、XML 文書の中で開始タグを検出するたびに呼び出され、そのタグが ITEM タグである場合にのみ新しい Message を作成します。次に characters メソッドを見てください。このメソッドは、テキスト・ノードの文字データを検出すると呼び出されます。その文字データは単に builder 変数に追加されます。最後に、endElement メソッドを見てください。このメソッドは終了タグを検出すると呼び出されます。Message のプロパティーに対応するタグ (TITLELINK など) の場合には、builder 変数のデータを使って適切なプロパティーが currentMessage に設定されます。終了タグが ITEM の場合には、currentMessage が Messages の一覧に追加されます。これらはすべて、SAX による構文解析では非常に典型的なことであり、ここでは Android に特有のことは何も行われていません。つまり、Java の SAX パーサーを作成する方法を知っていれば、Android の SAX パーサーを作成する方法も知っていることになります。ただし Android SDK には、SAX の上にいくつかの便利な機能が追加されています。


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 ハンドラーを使用していることに注目してください。そのため、上で説明した RssHandlerリスト 7 で再利用しています。SAX ハンドラーを再利用できるのは素晴らしいことですが、コードは少しばかり複雑です。先ほどの XML フィードよりもはるかに複雑な XML 文書を構文解析しなければならない場合を想像してみてください。そうした場合には、ハンドラーがバグの温床になる可能性があります。例えば、リスト 6endElement メソッドを再度見てください。このメソッドが、currentMessage がヌルかどうかをチェックしてからプロパティーを設定しようとしていることにお気づきでしょうか。そして今度はリスト 4 の XML の例を見てください。この場合には TITLE タグと LINK タグが ITEM タグの外にあることに注目してください。このために、ヌルのチェックをしているのです。ヌルのチェックをしないと、最初の 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 を使いたくない場合にはどうすればよいのでしょう。その場合にはいくつかの選択肢があります。その 1 つ目の選択肢として、DOM を調べることにします。


DOM を扱う

Android では DOM による構文解析が完全にサポートされています。Android での DOM は、デスクトップ・マシンまたはサーバー上で実行される Java コードでの DOM とまったく同じように動作します。リスト 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 ではすべてのものが最初にメモリーに読み込まれるため、一般的に DOM の方が多くのメモリーを消費します。これは Android が稼働するモバイル機器では問題かもしれませんが、XML 文書のサイズがあまり大きくならない特定の使い方では満足できるレベルかもしれません。想像ですが、Android の開発者達は、Android のアプリケーションでは SAX による構文解析の方がはるかに一般的であると推測したため、SAX による構文解析用に追加のユーティリティーが用意されているのかもしれません。Android では、もう 1 つ別のタイプの XML パーサーを使用することができます。それがプル・パーサーです。


XML プル・パーサー

先ほど触れたように、Android は Java の StAX API をサポートしていません。ただし Android には、StAX と同じように動作するプル・パーサーが用意されています。このプル・パーサーを使用すると、アプリケーション・コードがパーサーからイベントをプルしたり検索したりすることができます。これは、イベントを自動的にハンドラーにプッシュする SAX パーサーとは対照的です。リスト 10 はフィード・パーサー・インターフェースにプル・パーサーを実装した例です。

リスト 10. プル・パーサー・ベースの実装
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;
    }
}

プル・パーサーの動作は SAX パーサーの動作と似ています。プル・パーサーには、SAX パーサーの場合と同様のイベント (開始要素、終了要素) がありますが、それらのイベントからプルする必要があります (parser.next())。イベントは数字のコードとして送信されるため、単純な case-switch を使うことができます。注意する点として、プル・パーサーによる構文解析は SAX による構文解析のように要素の終了をリッスンするのではなく、大部分の処理を最初に行うという単純なものです。リスト 10 のコードの場合、要素が開始されるときに parser.nextText() を呼び出し、XML 文書からすべての文字データをプルしています。これは SAX による構文解析よりも見事に単純化されています。また、関心がある対象の最後に到達すると、最後まで到達したことを示すためにフラグ (ブール変数の done) をセットしていることにも注目してください。こうすることによって、その文書の残りはコードによる処理が行われない部分であることがわかるため、XML 文書の読み取りを早い段階で停止させることができます。これは、読み取り対象の XML 文書のごく一部分のみが必要な場合には非常に有効であり、できるだけ早い段階で構文解析を停止させることで、構文解析に要する時間を大幅に短縮することができます。繰り返しますが、この種の最適化は、接続速度が遅い場合もあるモバイル機器には特に重要です。プル・パーサーにはパフォーマンスの点でいくつかのメリットがあるだけではなく、使いやすさの点でも優れています。プル・パーサーは XML の作成にも使用することができます。


XML を作成する

ここまでは、インターネット上の XML の構文解析に焦点を絞ってきました。しかし場合によると、アプリケーションがリモート・サーバーに XML を送信しなければならないことがあります。もちろん、StringBuilder などを使って XML ストリングを作成することもできますが、別の選択肢としてプル・パーサーを使う方法があります (リスト 11)。

リスト 11. プル・パーサーを使って 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 と同じパッケージの一部です。このクラスはイベントをプルする代わりに、イベントをストリーム、つまりライターにプッシュします。この場合には、XmlSerializer クラスは単純にイベントを java.io.StringWriter インスタンスにプッシュしています。このクラスは、文書の開始と終了、要素の処理、そしてテキストまたは属性の追加のためのメソッドを備えた、単純な API として機能します。そのため、StringBuilder を使うよりも XmlSerializer クラスを使った方が便利です。XmlSerializer クラスを使えば XML は確実に整形式で作成されるからです。


まとめ

皆さんは Android 機器用に、どんなアプリケーションを作成してみたいでしょうか?そのアプリケーションがどのようなものであれ、インターネット上のデータを扱うのであれば、おそらくそのアプリケーションは XML を扱う必要があります。この記事では、Android には XML を扱うためのツールが豊富に用意されていることを説明しました。そうした中から、気に入ったツールを 1 つだけ選択することもでき、あるいは使い方に応じてさまざまなツールを使い分けることもできます。ほとんどの場合は SAX を選べば安全であり、Android には従来の方法での SAX の使い方と、SAX の上にラップされた便利で優れたラッパーを使う方法の両方が用意されています。文書が小さな場合には、おそらく DOM を使った方が簡単です。文書が大きく、ただしその文書の一部しか必要ない場合には、XML プル・パーサーを使った方が効率的かもしれません。そして最後に、XML を作成する場合、プル・パーサー・パッケージを利用すると XML の作成も手軽に行えることを説明しました。つまり、XML に関してどのようなものが必要な場合にも、Android SDK は必ず何かを提供してくれるのです。


ダウンロード

内容ファイル名サイズ
AndroidXml.zip70KB

参考文献

学ぶために

  • Develop Android applications with Eclipse」(Frank Ableson 著、developerWorks、2008年2月) を読んでください。Android アプリケーションを作成するために最も容易な方法は Eclipse を使うことです。このチュートリアルでは、そのすべてを学ぶことができます。
  • SAX、DOM、そしてプル・パーサーを比較した記事として、「Geronimo への転向: 統合パッケージを使用する: Codehaus の Woodstox」(Michael Galpin 著、developerWorks、2007年7月) も読んでください。
  • StAX で XML を処理する 第 2 回: プル解析とイベント」(Peter Nehrer 著、developerWorks、2006年12月) は XML プル・パーサーを詳細に説明しています。
  • このチュートリアル、「Understanding SAX」(Nicholas Chase 著、developerWorks、2003年7月) を読み、SAX による構文解析のエキスパートになってください。
  • このチュートリアル、「Understanding DOM」(Nicholas Chase 著、developerWorks、2007年3月) では、DOM による構文解析について詳細に説明しています。
  • Android SDK のドキュメントを読み、アプリケーション・コードの作成とデバッグ、そしてアプリケーションの UI を設計するためのツール・セットである Android SDK について学んでください。
  • Open Handset Alliance について調べてください。Open Handset Alliance は Android のスポンサーであり、モバイル技術の革新を加速するために協力する 47 社で構成されています。
  • XML および関連技術において IBM 認定技術者になる方法については、IBM XML certification を参照してください。
  • developerWorks の XML ゾーンを XML の技術ライブラリーとして利用してください。広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM Redbooks などが用意されています。
  • developerWorks の Technical events and webcasts で最新情報を入手してください。
  • developerWorks podcasts ではソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。

製品や技術を入手するために

  • Android 開発者の公式サイトから Android SDK をダウンロードし、API の資料にアクセスし、そして Android の最新ニュースを入手してください。
  • Android Open Source Project から Android のオープンソース・コードを入手してください。
  • Eclipse IDE の最新バージョンを取得し、活用してください。
  • IBM 製品の試用版をダウンロードするか、あるいはオンラインで IBM SOA Sandbox を試し、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Open source
ArticleID=415471
ArticleTitle=Android で XML を扱う
publish-date=06232009