一.基础知识

通过前面的学习我们已经知道了Android上使用SAX和DOM方式解析XML的方法,并且对两种做了简单的比较,通过比较我们知道对在往往内存比较稀缺的移动设备上运行的Android系统来说,SAX是一种比较合适的XML解析方式。

但是SAX方式的特点是需要解析完整个文档才会返回,如果在一个XML文档中我们只需要前面一部分数据,但是使用SAX方式还是会对整个文档进行解析,尽管XML文档中后面的大部分数据我们其实都不需要解析,因此这样实际上就浪费了处理资源。

就以USGS的地震数据为例,USGS网上的这个数据是定时更新的,但是一次更新往往只更新前面几条地震数据,大部分数据还是相同的,因此我们在解析时可以在上一次解析的结果之上根据<updated>元素标签中的值解析前面几条比之前updated值更新的地震数据即可。但是如果使用SAX方式的话,每次还是都会解析整个XML文档,而这却浪费了处理器资源和延长了处理的时间。

不过Android系统还提供了另一种XML解析方式可以使你更好的处理这种情况,就是Pull方式解析XML数据。

Pull解析器和SAX解析器虽有区别但也有相似性。他们的区别为:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别。

而他们的相似性在运行方式上,Pull解析器也提供了类似SAX的事件(开始文档START_DOCUMENT和结束文档END_DOCUMENT,开始元素START_TAG和结束元素END_TAG,遇到元素内容TEXT等),但需要调用next()方法提取它们(主动提取事件)。

Android系统中和Pull方式相关的包为org.xmlpull.v1,在这个包中提供了Pull解析器的工厂类XmlPullParserFactory和Pull解析器XmlPullParser,XmlPullParserFactory实例调用newPullParser方法创建XmlPullParser解析器实例,接着XmlPullParser实例就可以调用getEventType()和next()等方法依次主动提取事件,并根据提取的事件类型进行相应的逻辑处理。

下面我们就用上面介绍的Pull方式来实现解析XML形式的USGS地震数据的Demo例子。

二.实例开发

我们要完成的效果图如下图1所示:

Android XML解析学习——Pull方式_第1张图片

和上一部分Demo例子的一样,也是解析完地震数据后用ListView列表的方式显示每条地震的震级和地名信息。

新建一个Android工程AndroidXMLDemoPull。

要添加的基本内容和上一个Demo中的一样,这里就不再赘述,这次要添加的解析器新类为PullEarthquakeHandler,内容如下所示:

public   class  PullEarthquakeHandler {      //xml解析用到的Tag       private  String kEntryElementName =  "entry" ;      private  String kLinkElementName =  "link" ;      private  String kTitleElementName =  "title" ;      private  String kUpdatedElementName =  "updated" ;      private  String kGeoRSSPointElementName =  "georss:point" ;      private  String kGeoRSSElevElementName =  "georss:elev" ;      //用于保存xml解析获取的结果       private  ArrayList<EarthquakeEntry> earthquakeEntryList =  null ;      private  EarthquakeEntry earthquakeEntry =  null ;       private  Boolean startEntryElementFlag =  false ;      //解析xml数据       public  ArrayList<EarthquakeEntry> parse(InputStream inStream)      {          try  {                          //创建XmlPullParser,有两种方式               //方式一:使用工厂类XmlPullParserFactory               XmlPullParserFactory pullFactory = XmlPullParserFactory.newInstance();              XmlPullParser xmlPullParser = pullFactory.newPullParser();  //          //方式二:使用Android提供的实用工具类android.util.Xml   //          XmlPullParser xmlPullParser = Xml.newPullParser();               xmlPullParser.setInput(inStream, "UTF-8" );              int  eventType = xmlPullParser.getEventType();              boolean  isDone =  false ;               //具体解析xml               while  ((eventType != XmlPullParser.END_DOCUMENT)&&(isDone !=  true )) {                  String localName = null ;                  switch  (eventType) {                      case  XmlPullParser.START_DOCUMENT:                      {                          earthquakeEntryList = new  ArrayList<EarthquakeEntry>();                      }                          break ;                      case  XmlPullParser.START_TAG:                      {                          localName = xmlPullParser.getName();                          if (localName.equalsIgnoreCase(kEntryElementName))                          {                              earthquakeEntry = new  EarthquakeEntry();                              startEntryElementFlag = true ;                          }                          else   if (startEntryElementFlag ==  true )                          {                              String currentData = null ;                              if (localName.equalsIgnoreCase(kTitleElementName))                              {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                   //提取强度信息                                   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);                                                              }                              else   if  (localName.equalsIgnoreCase(kUpdatedElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                   //构造更新时间                                   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);                              }                              else   if  (localName.equalsIgnoreCase(kGeoRSSPointElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                   //提取经纬度信息                                   String[] latLongitude = currentData.split(" " );                                  Location location = new  Location( "dummyGPS" );                                  location.setLatitude(Double.parseDouble(latLongitude[0 ]));                                  location.setLongitude(Double.parseDouble(latLongitude[1 ]));                                  earthquakeEntry.setLocation(location);                              }                              else   if  (localName.equalsIgnoreCase(kGeoRSSElevElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                   //提取海拔高度信息                                   double  evel;                                  //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号                                   try  {                                      evel = Double.parseDouble(currentData);                                  } catch  (Exception e) {                                      // TODO: handle exception                                       e.printStackTrace();                                      evel = 0 ;                                  }                                  Log.v("Pull_Elev" , String.valueOf(evel));                                  earthquakeEntry.setElev(evel);                              }                              else   if (localName.equalsIgnoreCase(kLinkElementName))                              {                                  String webLink = xmlPullParser.getAttributeValue("" ,  "href" );                                  earthquakeEntry.setLink(webLink);  //                              Log.v("Pull", webLink);                               }                          }                                         }                          break ;                      case  XmlPullParser.END_TAG:                      {                          localName = xmlPullParser.getName();                          if ((localName.equalsIgnoreCase(kEntryElementName))&&(startEntryElementFlag== true ))                          {                              earthquakeEntryList.add(earthquakeEntry);                              startEntryElementFlag = false ;                          }                      }                          break ;                      default :                          break ;                      }                  eventType = xmlPullParser.next();              }          } catch  (Exception e) {              // TODO Auto-generated catch block               e.printStackTrace();          }          Log.v("Pull" ,  "End" );          return  earthquakeEntryList;      }  }  

public class PullEarthquakeHandler {      //xml解析用到的Tag      private String kEntryElementName = "entry";      private String kLinkElementName = "link";      private String kTitleElementName = "title";      private String kUpdatedElementName = "updated";      private String kGeoRSSPointElementName = "georss:point";      private String kGeoRSSElevElementName = "georss:elev";      //用于保存xml解析获取的结果      private ArrayList<EarthquakeEntry> earthquakeEntryList = null;      private EarthquakeEntry earthquakeEntry = null;       private Boolean startEntryElementFlag = false;      //解析xml数据      public ArrayList<EarthquakeEntry> parse(InputStream inStream)      {          try {                          //创建XmlPullParser,有两种方式              //方式一:使用工厂类XmlPullParserFactory              XmlPullParserFactory pullFactory = XmlPullParserFactory.newInstance();              XmlPullParser xmlPullParser = pullFactory.newPullParser();  //          //方式二:使用Android提供的实用工具类android.util.Xml  //          XmlPullParser xmlPullParser = Xml.newPullParser();              xmlPullParser.setInput(inStream, "UTF-8");              int eventType = xmlPullParser.getEventType();              boolean isDone = false;               //具体解析xml              while ((eventType != XmlPullParser.END_DOCUMENT)&&(isDone != true)) {                  String localName = null;                  switch (eventType) {                      case XmlPullParser.START_DOCUMENT:                      {                          earthquakeEntryList = new ArrayList<EarthquakeEntry>();                      }                          break;                      case XmlPullParser.START_TAG:                      {                          localName = xmlPullParser.getName();                          if(localName.equalsIgnoreCase(kEntryElementName))                          {                              earthquakeEntry = new EarthquakeEntry();                              startEntryElementFlag = true;                          }                          else if(startEntryElementFlag == true)                          {                              String currentData = null;                              if(localName.equalsIgnoreCase(kTitleElementName))                              {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                  //提取强度信息                                  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);                                                              }                              else if (localName.equalsIgnoreCase(kUpdatedElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                  //构造更新时间                                  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);                              }                              else if (localName.equalsIgnoreCase(kGeoRSSPointElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                  //提取经纬度信息                                  String[] latLongitude = currentData.split(" ");                                  Location location = new Location("dummyGPS");                                  location.setLatitude(Double.parseDouble(latLongitude[0]));                                  location.setLongitude(Double.parseDouble(latLongitude[1]));                                  earthquakeEntry.setLocation(location);                              }                              else if (localName.equalsIgnoreCase(kGeoRSSElevElementName)) {                                  currentData = xmlPullParser.nextText();  //                              Log.v("Pull", currentData);                                  //提取海拔高度信息                                  double evel;                                  //因为USGS数据有可能会输错,比如为"--10000",多了一个"-"号                                  try {                                      evel = Double.parseDouble(currentData);                                  } catch (Exception e) {                                      // TODO: handle exception                                      e.printStackTrace();                                      evel = 0;                                  }                                  Log.v("Pull_Elev", String.valueOf(evel));                                  earthquakeEntry.setElev(evel);                              }                              else if(localName.equalsIgnoreCase(kLinkElementName))                              {                                  String webLink = xmlPullParser.getAttributeValue("", "href");                                  earthquakeEntry.setLink(webLink);  //                              Log.v("Pull", webLink);                              }                          }                                         }                          break;                      case XmlPullParser.END_TAG:                      {                          localName = xmlPullParser.getName();                          if((localName.equalsIgnoreCase(kEntryElementName))&&(startEntryElementFlag==true))                          {                              earthquakeEntryList.add(earthquakeEntry);                              startEntryElementFlag = false;                          }                      }                          break;                      default:                          break;                      }                  eventType = xmlPullParser.next();              }          } catch (Exception e) {              // TODO Auto-generated catch block              e.printStackTrace();          }          Log.v("Pull", "End");          return earthquakeEntryList;      }  }  

程序首先也是定义解析用到的变量,在定义的用于解析xml数据的方法中

publicArrayList<EarthquakeEntry>parse(InputStream inStream)

定义了一个局部变量

booleanisDone =false;

用于标志在有满足条件时停止读取XML文档,退出解析过程。

主体部分首先创建XmlPullParser,


//创建XmlPullParser,有两种方式               //方式一:使用工厂类XmlPullParserFactory               XmlPullParserFactory pullFactory = XmlPullParserFactory.newInstance();              XmlPullParser xmlPullParser = pullFactory.newPullParser();  //          //方式二:使用Android提供的实用工具类android.util.Xml   //          XmlPullParser xmlPullParser = Xml.newPullParser();   

//创建XmlPullParser,有两种方式              //方式一:使用工厂类XmlPullParserFactory              XmlPullParserFactory pullFactory = XmlPullParserFactory.newInstance();              XmlPullParser xmlPullParser = pullFactory.newPullParser();  //          //方式二:使用Android提供的实用工具类android.util.Xml  //          XmlPullParser xmlPullParser = Xml.newPullParser(); 

创建XmlPullParser有两种方式,一种是使用我们介绍的org.xmlpull.v1包中的工厂类XmlPullParserFactory。除了这种方式外,还可以使用Android SDK提供的实用工具包android.util中的类Xml的newPullParser()方法直接创建。

接着为pull解析器设置要解析的xml文档数据,并使用主动的方式获取解析器中的事件,


xmlPullParser.setInput(inStream,  "UTF-8" );  int  eventType = xmlPullParser.getEventType();  

xmlPullParser.setInput(inStream, "UTF-8");  int eventType = xmlPullParser.getEventType();  

事件将作为数值代码被发送,因此可以使用一个简单case-switch或者if-else对事件的类型(START_TAG, END_TAG, TEXT或者其他等)进行判断,并根据对应的事件类型实现相应的处理逻辑。解析并未像SAX解析那样监听元素的结束,而是在开始处(START_TAG)完成了大部分处理,因为当某个元素开始时,可以调用解析器实例的nextText()从XML文档中提取所有字符数据,

currentData= xmlPullParser.nextText();

当一个事件处理完成后,可以调用next()方法主动从解析器中获取下一个事件,

eventType = xmlPullParser.next();

整个过程可以放在一个while循环中,直到碰到XML文档的结束事件,

while((eventType != XmlPullParser.END_DOCUMENT)&&(isDone !=true))

或者是设置的条件满足主动停止解析过程,可以看到在上面的while条件中除了碰到文档结束的条件外,

(eventType != XmlPullParser.END_DOCUMENT)

还有设置的标志是否满足的条件,

(isDone !=true)

设置一个标记(布尔变量isDone)来确定何时到达感兴趣内容的结束部分,允许我们提早停止读取XML文档,因为我们知道代码将不会关心文档的其余部分。这有时非常实用,特别是当我们只需要访问一小部分XML文档时。通过尽快停止解析,我们可以极大地减少解析时间。这种优化对于Android系统运行的速度较慢的移动设备尤为重要。因此Pull解析器可以提供一些性能优势以及易用性。

最后添加AndroidXMLDemoPull.java文件中的内容,内容和前一个Demo工程AndroidXMLDemoDom中的AndroidXMLDemoDom.java基本一样,

public   class  AndroidXMLDemoPull  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();          //Pull方式进行xml解析           PullEarthquakeHandler pullHandler = new  PullEarthquakeHandler();          earthquakeEntryList = pullHandler.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;      }  }  

public class AndroidXMLDemoPull 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();          //Pull方式进行xml解析          PullEarthquakeHandler pullHandler = new PullEarthquakeHandler();          earthquakeEntryList = pullHandler.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;      }  }  

只是把进行XML解析的部分换成了如下方式:

/Pull方式进行xml解析   PullEarthquakeHandler pullHandler = new  PullEarthquakeHandler();  earthquakeEntryList = pullHandler.parse(earthquakeStream);  

//Pull方式进行xml解析  PullEarthquakeHandler pullHandler = new PullEarthquakeHandler();  earthquakeEntryList = pullHandler.parse(earthquakeStream); 

完成了,可以保存运行看下效果。

三.总结

在这部分中我们学习了Android平台上除了SAX和DOM方式外的第三种解析XML的方式,即使用Pull解析器的方式,并且介绍了Pull方式和SAX方式比较的特点,就是可以在程序中使用代码主动从解析器中提取事件且可以提前停止XML文档的解析。并用这个方式完成了一个解析USGS地震数据的Demo例子。

这样我们就学习了Android平台上的三种解析XML的方式:SAX、DOM和Pull,这三种方式除了我们已经学习过的各自的特点外,他们的性能比较如何?那个解析的速度最快?这部分内容我们以后接着学习。



更多相关文章

  1. [android盈利模式探索]我也分享一下我Android的收入数据
  2. Android - 事件分发机制
  3. Android开发之Jsoup解析webView加载数据
  4. Android Fragment内嵌Fragment页面不刷新数据问题
  5. Android java.net.MalformedURLException: no protocol: 解决方
  6. Android之Activity的几种跳转方式
  7. android 4.0.x Home键事件拦截/监听
  8. android 测试读取LEB数据的函数

随机推荐

  1. android中dialog工具类的实现(多种dialog
  2. android根据包名获取签名MD5信息
  3. android HTTP post方法时,如何使用cookies
  4. Android中如何使用对话框(单选对话框和多
  5. Android 如何让Dialog实现背景透明
  6. Android(安卓)sensor 大杂烩
  7. Android之自定义标题栏(组合控件)
  8. slidingmenu框架
  9. android使用Canvas画柱状图和饼图源码
  10. android弹出单选、多选菜单