图1 ListView列表显示的地震数据
和上一部分Demo例子的一样,也是解析完地震数据后用ListView列表的方式显示每条地震的震级和地名信息。
新建一个Android工程AndroidXMLDemoSaxII。
添加进上一个Demo工程AndroidXMLDemoSax中的EarthquakeEntry.java文件,如果需要从本地读取xml数据的话,同时在assets文件夹下添加保存为xml格式了的USGS地震数据USGS_Earthquake_1M2_5.xml,如果需要联网读取的话,在manifest.xml文件中添加权限:
- <uses-permission android:name="android.permission.INTERNET" />
并修改res/layout下的main.xml为:
- <?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"
- >
- <ListView
- android:id="@+id/list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
- </LinearLayout>
接下来就来新建添加一个类AndroidSaxEarthquakeHandler,以Android SDK提供的包android.sdk的API来完成解析地震数据的具体逻辑实现,内容如下:
- public class AndroidSaxEarthquakeHandler {
- //xml解析用到的Tag
- private String kRootElementName = "feed";
- private String kEntryElementName = "entry";
- private String kLinkElementName = "link";
- private String kTitleElementName = "title";
- private String kUpdatedElementName = "updated";
- private String kGeoRSSPointElementName = "point";
- private String kGeoRSSElevElementName = "elev";
- static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
- static final String GEORSS_NAMESPACE = "http://www.georss.org/georss";
- //用于保存xml解析获取的结果
- private ArrayList<EarthquakeEntry> earthquakeEntryList;
- private EarthquakeEntry earthquakeEntry;
- //解析xml数据
- public ArrayList<EarthquakeEntry> parse(InputStream inStream)
- {
- earthquakeEntryList = new ArrayList<EarthquakeEntry>();
- RootElement root = new RootElement(ATOM_NAMESPACE, kRootElementName);
- Element entry = root.getChild(ATOM_NAMESPACE, kEntryElementName);
- //具体解析xml
- //处理entry标签
- entry.setStartElementListener(new StartElementListener() {
- @Override
- public void start(Attributes attributes) {
- // TODO Auto-generated method stub
- earthquakeEntry = new EarthquakeEntry();
- }
- });
- entry.setEndElementListener(new EndElementListener() {
- @Override
- public void end() {
- // TODO Auto-generated method stub
- earthquakeEntryList.add(earthquakeEntry);
- }
- });
- //处理title标签
- entry.getChild(ATOM_NAMESPACE, kTitleElementName).setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String currentData) {
- // TODO Auto-generated method stub
- //提取强度信息
- String magnitudeString = currentData.split(" ")[1];
- int end = magnitudeString.length()-1;
- magnitudeString = magnitudeString.substring(0, end);
- double magnitude = Double.parseDouble(magnitudeString);
- earthquakeEntry.setMagnitude(magnitude);
- //提取位置信息
- String place = currentData.split(",")[1].trim();
- earthquakeEntry.setPlace(place);
- }
- });
- //处理updated标签
- entry.getChild(ATOM_NAMESPACE, kUpdatedElementName).setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String currentData) {
- // TODO Auto-generated method stub
- //构造更新时间
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
- Date qdate = new GregorianCalendar(0,0,0).getTime();
- try {
- qdate = sdf.parse(currentData);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- earthquakeEntry.setDate(qdate);
- }
- });
- //处理point标签
- entry.getChild(GEORSS_NAMESPACE, kGeoRSSPointElementName).setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String currentData) {
- // TODO Auto-generated method stub
- //提取经纬度信息
- String[] latLongitude = currentData.split(" ");
- Location location = new Location("dummyGPS");
- location.setLatitude(Double.parseDouble(latLongitude[0]));
- location.setLongitude(Double.parseDouble(latLongitude[1]));
- earthquakeEntry.setLocation(location);
- }
- });
- //处理elev标签
- entry.getChild(GEORSS_NAMESPACE, kGeoRSSElevElementName).setEndTextElementListener(new EndTextElementListener() {
- @Override
- public void end(String currentData) {
- // TODO Auto-generated method stub
- //提取海拔高度信息
- double evel;
- //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号
- try {
- evel = Double.parseDouble(currentData);
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- evel = 0;
- }
- Log.v("Sax_Elev", String.valueOf(evel));
- earthquakeEntry.setElev(evel);
- }
- });
- //处理link标签
- entry.getChild(ATOM_NAMESPACE, kLinkElementName).setStartElementListener(new StartElementListener() {
- @Override
- public void start(Attributes attributes) {
- // TODO Auto-generated method stub
- //获取link链接
- String webLink = attributes.getValue("href");
- earthquakeEntry.setLink(webLink);
- }
- });
- //调用android.util.Xml开始解析
- try {
- Xml.parse(inStream, Xml.Encoding.UTF_8, root.getContentHandler());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return earthquakeEntryList;
- }
- }
开头处定义了解析需要的元素标签名称,因为getChild方法获取子元素时需要命名空间,因此还新定义了USGS的xml数据中包含的两个命名空间:
- static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
- static final String GEORSS_NAMESPACE = "http://www.georss.org/georss";
在定义的用于解析xml数据的方法中
public ArrayList<EarthquakeEntry> parse(InputStream inStream)
首先获取xml文档的根元素:
RootElement root = new RootElement(ATOM_NAMESPACE, kRootElementName);
有了根元素之后,就可以使用类似DOM的getChild方法获取具体的某个位置处的子元素,并且可以为具体的子元素注册事件处理器和在对应的回调函数中实现具体的处理逻辑。
从程序中我们可以看到,我们只为<entry>元素和<entry></entry>包含的子元素注册了事件处理器,因此即使xml文档开始处有<updated>、<title>和<link>等同名的元素标签,但也不会进行处理。因此和上一部分的Demo相比,就不再需要设置标志变量用来判断,而且看起来也更加简单了。
虽然写法不同了,但是对具体标签的处理逻辑和上一部分Demo中是一样的,因为处理的xml文档内容没有变。
程序的最后调用android.util.Xml类的类方法parse直接进行解析,也更加方便了。
- //调用android.util.Xml开始解析
- try {
- Xml.parse(inStream, Xml.Encoding.UTF_8, root.getContentHandler());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
其中Xml类parse方法的ContentHandler参数由根元素通过getContentHandler()方式获得。
最后添加AndroidXMLDemoSaxII.java文件中的内容,内容和上一个Demo工程AndroidXMLDemoSax中的AndroidXMLDemoSax.java基本一样,
- public class AndroidXMLDemoSaxII extends Activity {
- /** Called when the activity is first created. */
- //定义显示的List相关变量
- ListView list;
- ArrayAdapter<EarthquakeEntry> adapter;
- ArrayList<EarthquakeEntry> earthquakeEntryList;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //获取地震数据流
- InputStream earthquakeStream = readEarthquakeDataFromFile();
- //Android Sax方式进行解析
- AndroidSaxEarthquakeHandler androidSaxHandler = new AndroidSaxEarthquakeHandler();
- earthquakeEntryList = androidSaxHandler.parse(earthquakeStream);
- //用ListView进行显示
- list = (ListView)this.findViewById(R.id.list);
- adapter = new ArrayAdapter<EarthquakeEntry>(this, android.R.layout.simple_list_item_1, earthquakeEntryList);
- list.setAdapter(adapter);
- }
- private InputStream readEarthquakeDataFromFile()
- {
- //从本地获取地震数据
- InputStream inStream = null;
- try {
- inStream = this.getAssets().open("USGS_Earthquake_1M2_5.xml");
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return inStream;
- }
- private InputStream readEarthquakeDataFromInternet()
- {
- //从网络上获取实时地震数据
- URL infoUrl = null;
- InputStream inStream = null;
- try {
- infoUrl = new URL("http://earthquake.usgs.gov/earthquakes/catalogs/1day-M2.5.xml");
- URLConnection connection = infoUrl.openConnection();
- HttpURLConnection httpConnection = (HttpURLConnection)connection;
- int responseCode = httpConnection.getResponseCode();
- if(responseCode == HttpURLConnection.HTTP_OK)
- {
- inStream = httpConnection.getInputStream();
- }
- } catch (MalformedURLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return inStream;
- }
- }
只是把进行SAX解析的部分换成了如下方式:
- //Android Sax方式进行解析
- AndroidSaxEarthquakeHandler androidSaxHandler = new AndroidSaxEarthquakeHandler();
- earthquakeEntryList = androidSaxHandler.parse(earthquakeStream);
完成了,可以保存运行看下效果。
三.总结
Android平台提供了相当强大的XML解析支持,不仅包含了Java SDK中用来XML处理的API,而且Android SDK还提供了特有的用于SAX解析XML的包android.sax。使用这个包中的API可以更加方便解析,特别是当要解析的xml文档中在不同的层级位置处有多个相同名称的标签但需要分别进行不同处理时,同时也有更好的鲁棒性,减少解析时产生Bug的可能性。
以上我们介绍的都是SAX方式解析XML,而解析XML常用的还有DOM方式,这部分内容我们以后接着学习