需要注意的是,样例应用程序没有实时地从 web 移动数据,尽管我们期盼一个真实应用程序。由于数据 feeds 取自原始资源文件夹,因此应用程序关注的是分析方面。关于使用 Android 进行网络连接相关信息的链接,见 。
Android 应用程序很简单。其中包含 XML 和 JSON 数据资料的全部副本。用户可以任选其一进行分析。 显示 Eclipse 中项目文件的结构。(查看 图 2 的文本版本。)
图 2. Eclipse 项目的文件结构显示选择解析选项之前的应用程序 UI:
图 3. 选择解析选项之前的应用程序 UI应用程序 UI 有两个按钮,Parse XML 和 Parse JSON,接着是默认文本。 包含该 UI 的布局,在项目的 res/layout 文件夹中的 main.xml 中可以找到:
清单 6. UI 的布局<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnXML" android:text="Parse XML"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnJSON" android:text="Parse JSON file"></Button> </LinearLayout> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ScrollView01" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="default text" android:layout_gravity="center_horizontal" android:id="@+id/txtData" /> </ScrollView> </LinearLayout>
Parse XML 和 Parse JSON file 按钮是在 ScrollView 之上定义的,ScrollView 中含有一个 TextView 控件。这里的意思是想要用户可以滚动结果数据。
注意多个 LinearLayout 结构的使用。第一个是垂直对齐,其中既含有一个带有水平结构的 LinearLayout 和一个 ScrollView。内层 LinearLayout 含有两个 Button 小部件。这个布局很冗长,一般在 onCreate() 方法中调用,见 :
清单 7. onCreate() 方法Button btnXML; Button btnJSON; TextView tvData; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tvData = (TextView) findViewById(R.id.txtData); btnXML = (Button) findViewById(R.id.btnXML); btnXML.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { examineXMLFile(); } }); btnJSON = (Button) findViewById(R.id.btnJSON); btnJSON.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { examineJSONFile(); } }); }
examineXMLFile() 方法控制 XML 解析。
XML 解析SAX 对 DOM
Android 也支持一个 DOM 解析器,这需要占用较大的内存空间,但是降低在 SAX 解析器中的复杂性。对于 XMLvsJSON 这个应用程序,仅仅对大数据资源很小的一个子集感兴趣,SAX 方法可能是处理作业的最好工具。
XML 数据解析通常是使用一个 SAX 风格的解析器完成的。对于这类解析器,您可以建立一个 InputSource 指向源 XML 数据,并在文档 “运行” 时提供一个接收某些事件的处理程序。 展示 examineXMLFile() 方法,执行以下任务:
清单 8. examineXMLFIle() 方法void examineXMLFile() { try { InputSource is = new InputSource(getResources() .openRawResource(R.raw.xmltwitter)); // create the factory SAXParserFactory factory = SAXParserFactory.newInstance(); // create a parser SAXParser parser = factory.newSAXParser(); // create the reader (scanner) XMLReader xmlreader = parser.getXMLReader(); // instantiate our handler twitterFeedHandler tfh = new twitterFeedHandler(); // assign our handler xmlreader.setContentHandler(tfh); // perform the synchronous parse xmlreader.parse(is); // should be done... let's display our results tvData.setText(tfh.getResults()); } catch (Exception e) { tvData.setText(e.getMessage()); } }
虽然 examineXMLFile() 设置好了一切,但应用程序角度来看,真正的解析工作实际是在处理程序中进行的,该处理程序是在 twitterFeedHandler.java 文件中实现的。实现 DefaultHandler 接口的类见 :
清单 9. twitterFeedHandler 类public class twitterFeedHandler extends DefaultHandler { StringBuilder sb = null; String ret = ""; boolean bStore = false; int howMany = 0; twitterFeedHandler() { } String getResults() { return "XML parsed data.\nThere are [" + howMany + "] status updates\n\n" + ret; } @Override public void startDocument() throws SAXException { // initialize "list" } @Override public void endDocument() throws SAXException { } @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { try { if (localName.equals("status")) { this.sb = new StringBuilder(""); bStore = true; } if (localName.equals("user")) { bStore = false; } if (localName.equals("text")) { this.sb = new StringBuilder(""); } if (localName.equals("created_at")) { this.sb = new StringBuilder(""); } } catch (Exception ee) { Log.d("error in startElement", ee.getStackTrace().toString()); } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (bStore) { if (localName.equals("created_at")) { ret += "Date: " + sb.toString() + "\n"; sb = new StringBuilder(""); return; } if (localName.equals("user")) { bStore = true; } if (localName.equals("text")) { ret += "Post: " + sb.toString() + "\n\n"; sb = new StringBuilder(""); return; } } if (localName.equals("status")) { howMany++; bStore = false; } } @Override public void characters(char ch[], int start, int length) { if (bStore) { String theString = new String(ch, start, length); this.sb.append(theString); } } }