XmlPullParser
, 它是在Android上一个高效且可维护的解析XML方法。 Android 上有这个接口的两种实现方式:
KXmlParser
viaXmlPullParserFactory.newPullParser()
. ExpatPullParser
, viaXml.newPullParser()
. ExpatPullParser
, viaXml.newPullParser()
.
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ..."> <title type="text">newest questions tagged android - Stack Overflow</title> ... <entry> ... </entry> <entry> <id>http://stackoverflow.com/q/9439999</id> <re:rank scheme="http://stackoverflow.com">0</re:rank> <title type="text">Where is my data file?</title> <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/> <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/> <author> <name>cliff2310</name> <uri>http://stackoverflow.com/users/1128925</uri> </author> <link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" /> <published>2012-02-25T00:30:54Z</published> <updated>2012-02-25T00:30:54Z</updated> <summary type="html"> <p>I have an Application that requires a data file...</p> </summary> </entry> <entry> ... </entry> ... </feed>
entry
标签与它的子标签title
,link
, summary
.public class StackOverflowXmlParser { // We don't use namespaces private static final String ns = null; public List parse(InputStream in) throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); parser.setInput(in, null); parser.nextTag(); return readFeed(parser); } finally { in.close(); } } ... }
readFeed()
实际上并没有处理feed的内容。它只是在寻找一个 "entry"的标签作为递归(recursively)处理整个feed的起点。如果一个标签它不是"entry"
, readFeed()方法会跳过它. 当整个feed都被递归处理后,readFeed()
会返回一个包含了entry标签(包括里面的数据成员)的List
。
private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException { List entries = new ArrayList(); parser.require(XmlPullParser.START_TAG, ns, "feed"); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } String name = parser.getName(); // Starts by looking for the entry tag if (name.equals("entry")) { entries.add(readEntry(parser)); } else { skip(parser); } } return entries; }
解析步骤如下:
entry
标签与它的内部标签title
,link
, summary
.
readEntry()
,readTitle() 等等
. 解析器从input stream中读取tag . 当读取到entry
,title
,link
或者summary
标签时,它会为那些标签调用相应的方法,否则,跳过这个标签。title
andsummary
tags, 解析器调用readText()
. 通过调用parser.getText()
.来获取返回数据。link
tag, 解析器先判断这个link是否是我们想要的类型,然后再读取数据。可以使用parser.getAttributeValue()
来获取返回数据。entry
tag, 解析起调用readEntry()
. 这个方法解析entry的内部标签并返回一个带有title
,link
, andsummary
数据成员的Entry对象。skip()
. 关于这部分的讨论,请看下面一部分内容:Skip Tags You Don't Care About 下面的代码演示了如何解析 entries, titles, links, 与 summaries.
public static class Entry { public final String title; public final String link; public final String summary; private Entry(String title, String summary, String link) { this.title = title; this.summary = summary; this.link = link; } } // Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off // to their respective "read" methods for processing. Otherwise, skips the tag. private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, ns, "entry"); String title = null; String summary = null; String link = null; while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } String name = parser.getName(); if (name.equals("title")) { title = readTitle(parser); } else if (name.equals("summary")) { summary = readSummary(parser); } else if (name.equals("link")) { link = readLink(parser); } else { skip(parser); } } return new Entry(title, summary, link); } // Processes title tags in the feed. private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, ns, "title"); String title = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "title"); return title; } // Processes link tags in the feed. private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException { String link = ""; parser.require(XmlPullParser.START_TAG, ns, "link"); String tag = parser.getName(); String relType = parser.getAttributeValue(null, "rel"); if (tag.equals("link")) { if (relType.equals("alternate")){ link = parser.getAttributeValue(null, "href"); parser.nextTag(); } } parser.require(XmlPullParser.END_TAG, ns, "link"); return link; } // Processes summary tags in the feed. private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, ns, "summary"); String summary = readText(parser); parser.require(XmlPullParser.END_TAG, ns, "summary"); return summary; } // For the tags title and summary, extracts their text values. private String readText(XmlPullParser parser) throws IOException, XmlPullParserException { String result = ""; if (parser.next() == XmlPullParser.TEXT) { result = parser.getText(); parser.nextTag(); } return result; } ... }
skip()
方法:
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } int depth = 1; while (depth != 0) { switch (parser.next()) { case XmlPullParser.END_TAG: depth--; break; case XmlPullParser.START_TAG: depth++; break; } } }
START_TAG
.START_TAG
, and all events up to and including the matchingEND_TAG
.END_TAG
and not at the first tag it encounters after the originalSTART_TAG
, it keeps track of the nesting depth. depth
的值就不会为 0,直到解析器已经处理了所有位于START_TAG
与END_TAG
之间的事件。例如,看解析器如何跳过<author>
标签,它有2个子标签,<name>
与<uri>:
while
loop, the next tag the parser encounters after<author>
is theSTART_TAG
for<name>
. The value fordepth
is incremented to 2.while
loop, the next tag the parser encounters is theEND_TAG
</name>
. The value fordepth
is decremented to 1.while
loop, the next tag the parser encounters is theSTART_TAG
<uri>
. The value fordepth
is incremented to 2.while
loop, the next tag the parser encounters is theEND_TAG
</uri>
. The value fordepth
is decremented to 1.while
loop, the next tag the parser encounters is theEND_TAG
</author>
. The value fordepth
is decremented to 0, indicating that the<author>
element has been successfully skipped. 示例程序是在AsyncTask
中获取与解析XML数据的。当获取到数据后,程序会在main activity(NetworkActivity
)里面进行更新操作。
在下面示例代码中,loadPage()
方法做了下面的事情:
new DownloadXmlTask().execute(url)
. 这会初始化一个新的DownloadXmlTask(AsyncTask
subclass)
对象并且开始执行它的execute()
方法。 public class NetworkActivity extends Activity { public static final String WIFI = "Wi-Fi"; public static final String ANY = "Any"; private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"; // Whether there is a Wi-Fi connection. private static boolean wifiConnected = false; // Whether there is a mobile connection. private static boolean mobileConnected = false; // Whether the display should be refreshed. public static boolean refreshDisplay = true; public static String sPref = null; ... // Uses AsyncTask to download the XML feed from stackoverflow.com. public void loadPage() { if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) { new DownloadXmlTask().execute(URL); } else if ((sPref.equals(WIFI)) && (wifiConnected)) { new DownloadXmlTask().execute(URL); } else { // show error } }下面是 DownloadXmlTask是怎么工作的:
// Implementation of AsyncTask used to download XML feed from stackoverflow.com. private class DownloadXmlTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... urls) { try { return loadXmlFromNetwork(urls[0]); } catch (IOException e) { return getResources().getString(R.string.connection_error); } catch (XmlPullParserException e) { return getResources().getString(R.string.xml_error); } } @Override protected void onPostExecute(String result) { setContentView(R.layout.main); // Displays the HTML string in the UI via a WebView WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadData(result, "text/html", null); } }下面是 loadXmlFromNetwork 是怎么工作的:
// Uploads XML from stackoverflow.com, parses it, and combines it with // HTML markup. Returns HTML string.【这里可以看出应该是Download】 private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException { InputStream stream = null; // Instantiate the parser StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser(); List<Entry> entries = null; String title = null; String url = null; String summary = null; Calendar rightNow = Calendar.getInstance(); DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa"); // Checks whether the user set the preference to include summary text SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); boolean pref = sharedPrefs.getBoolean("summaryPref", false); StringBuilder htmlString = new StringBuilder(); htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>"); htmlString.append("<em>" + getResources().getString(R.string.updated) + " " + formatter.format(rightNow.getTime()) + "</em>"); try { stream = downloadUrl(urlString); entries = stackOverflowXmlParser.parse(stream); // Makes sure that the InputStream is closed after the app is // finished using it. } finally { if (stream != null) { stream.close(); } } // StackOverflowXmlParser returns a List (called "entries") of Entry objects. // Each Entry object represents a single post in the XML feed. // This section processes the entries list to combine each entry with HTML markup. // Each entry is displayed in the UI as a link that optionally includes // a text summary. for (Entry entry : entries) { htmlString.append("<p><a href='"); htmlString.append(entry.link); htmlString.append("'>" + entry.title + "</a></p>"); // If the user set the preference to include summary text, // adds it to the display. if (pref) { htmlString.append(entry.summary); } } return htmlString.toString(); } // Given a string representation of a URL, sets up a connection and gets // an input stream.【关于Timeout具体应该设置多少,可以借鉴这里的数据,当然前提是一般情况下】private InputStream downloadUrl(String urlString) throws IOException { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); // Starts the query conn.connect(); InputStream stream = conn.getInputStream(); }
学习自:http://developer.android.com/training/basics/network-ops/xml.html,请多指教,谢谢!
转载请注明作者与出自:http://blog.csdn.net/kesenhoo,谢谢配合!