黑马程序员_Mp3播放器之对xml文件的解析
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?------- android培训、java培训、期待与您交流! ----------
?
这一段的来历是这样,我的MP3播放器里面有一个在线音乐的activity,里面可以读取到远程服务器上,有哪些歌,只是有哪些歌,还没有把这些歌下载到手机里。当然远程服务器上也不是说放一个mp3文件,我们的android手机联网就可以轻易的只得到MP3文件的名字。实际上,是我们自己再远程服务器端写了一个xml文件。里面把我们所有的歌的信息自己归纳好,然后放在这个xml文件中,然后当手机播放器需要知道这个服务器上有什么歌的时候,我们只要把这个xml文件传过去就可以了,这样,这个xml文件又小,又小的话就方便传输,不会让客户等待的非常久,体验就会好。但是这个xml文件的制作要一定的规则,读取也要有一点的规则。注意:制造是在服务器端,读取是在手机客户端。那么我们先来看看一个已经制造好了的存在于服务器端的xml文件吧。
?
服务器端的xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?><resources><resource><id>0001</id><mp3.name>林宥嘉-说谎.mp3</mp3.name><mp3.size>4228879</mp3.size><lrc.name>林宥嘉-说谎.lrc</lrc.name><lrc.size>1153</lrc.size></resource><resource><id>0002</id><mp3.name>陈奕迅-想哭.mp3</mp3.name><mp3.size>3855213</mp3.size><lrc.name>陈奕迅-想哭.lrc</lrc.name><lrc.size>1785</lrc.size></resource><resource><id>0003</id><mp3.name>弦子-醉清风.mp3</mp3.name><mp3.size>6287536</mp3.size><lrc.name>弦子-醉清风.lrc</lrc.name><lrc.size>1481</lrc.size></resource></resources>
?接着,我们来看看客户端的代码。我的设计是,当我们点击播放器的在线音乐的activity的时候,会去下载服务器端的resources.xml文件,并解析内容,再显示。
?
?
首先,我们设计了一个mp3类。里面是设置一些属性。懂点英文的人都应该知道是什么意思
?
package com.music.mp3;import java.io.Serializable;public class Mp3 implements Serializable{private int id;private String mp3_name;private String mp3_size;private String lrc_name;private String lrc_size;public Mp3() {super();// TODO Auto-generated constructor stub}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getMp3_name() {return mp3_name;}public void setMp3_name(String mp3_name) {this.mp3_name = mp3_name;}public String getMp3_size() {return mp3_size;}public void setMp3_size(String mp3_size) {this.mp3_size = mp3_size;}public String getLrc_name() {return lrc_name;}public void setLrc_name(String lrc_name) {this.lrc_name = lrc_name;}public String getLrc_size() {return lrc_size;}public void setLrc_size(String lrc_size) {this.lrc_size = lrc_size;}@Overridepublic String toString() {return "Mp3 [id=" + id + ", mp3_name=" + mp3_name + ", mp3_size="+ mp3_size + ", lrc_name=" + lrc_name + ", lrc_size="+ lrc_size + "]";}public Mp3(int id, String mp3_name, String mp3_size, String lrc_name,String lrc_size) {super();this.id = id;this.mp3_name = mp3_name;this.mp3_size = mp3_size;this.lrc_name = lrc_name;this.lrc_size = lrc_size;}}
?1、当我们打算显示远程音乐的时候讲调用下面这个函数
private List<Mp3> mp3Infos=null;//更新的mp3列表就放在了这里面,用于显示远程服务端有哪些private void updateListView(){String xml=downloadXML("http://192.168.1.103:8080/mp3/resources.xml");//用一个字符串来接受返回的值System.out.println("xml:"+xml);mp3Infos=parse(xml);//对返回的字符串开始解析并且返回给一个list类型的对象//下面要做的是将接收到的内容显示到屏幕上//封装了函数,这样将形成SimpleAdapter的过程给封装了起来,让其显示在android上。下面这个方法我就不贴出来了。因为太常用了。setListAdapter(buildSimepleAdapter(mp3Infos));//组织好之后使用setListAdapter让其显示在ListActivity当中,因为这个页面的类是继承了ListActivity的}//2、downloadXML("http://192.168.1.103:8080/mp3/resources.xml");private String downloadXML(String urlStr){HttpDownloader httpdownloader=new HttpDownloader();//用另一个类的对象来调用函数String result=httpdownloader.download(urlStr);//用一个字符串对象来接受返回的值return result;}//3、httpdownloader.download(urlStr)/* * 根据URL下载文件, 前提是这个文件当中的内容是文本,函数的返回值就是文件当中的内容 * 1.创建一个url对象 * 2.通过url对象,创建一个HttpURLConnection的对象 * 3.得到InputStream * 4.从InputStream当中读取数据 */public String download(String urlStr){StringBuffer sb=new StringBuffer();String line=null;BufferedReader buffer=null;try {//1.创建一个URL对象url= new URL(urlStr);System.out.println("url:"+urlStr);//2.创建一个Http对象HttpURLConnection urlConn=(HttpURLConnection) url.openConnection();//使用IO流读取数据buffer=new BufferedReader(new InputStreamReader(urlConn.getInputStream(),"GB2312"));//buffer=new BufferedReader(new InputStreamReader(urlConn.getInputStream()));//InputStreamReader(in,"GB2312")while((line=buffer.readLine())!=null)sb.append(line);System.out.println("sb:"+sb);} catch (Exception e) {e.printStackTrace();// TODO: handle exception}finally{try {buffer.close();} catch (Exception e2) {e2.printStackTrace();// TODO: handle exception}}return sb.toString();//4、mp3Infos=parse(xml);private List<Mp3> parse(String xmlStr){//创建一个解析器的工厂saxParserFactroy。这句话没有为什么,就这样规定的SAXParserFactory saxParserFactroy=SAXParserFactory.newInstance();//感觉下句第一个List是个大类型,而后面用了ArrayList才是明确规定infos更细的类型List<Mp3> infos=new ArrayList<Mp3>();//System.Collections.ArrayList类是一个特殊的数组。通过添加和删除元素,就可以动态改变数组的长度。try {//使用saxParserFactroy来创建一个解析器对象。这句话没有为什么,就这样规定的XMLReader xmlReader=saxParserFactroy.newSAXParser().getXMLReader();//这是使用了Mp3ListContentHandler的构造方法,也只是传了一个空的infos,但是在后面会往里面放东西Mp3ListContentHandler mp3ListContentHandler =new Mp3ListContentHandler(infos);//下面这句是为xml设置内容处理器.这就是设置解析器xmlReader在实际解析过程中发生了事件如何处理。在这里就是用mp3ListContentHandler这个对象的类的里面的方法来实现 xmlReader.setContentHandler(mp3ListContentHandler);//这里表示开始解析文件。xmlStr就是我们要解析的内容,所以但是parse方法需要一个InputSource input的对象,所以我们后面的过程都是为了将下载下来的xmlStr转换成一个InputSource的对象xmlReader.parse(new InputSource(new StringReader(xmlStr)));// 使用迭代器的标准方式:使用next依次访问迭代器中的每一项,直到hasNext返回false,此时已到达迭代器的末尾//感觉就是对infos使用迭代器。并且打印出infos里面的每一个元素,看看我们读取的而是否正确for (Iterator iterator =infos.iterator();iterator.hasNext();){//注意上句是判断,如果有下一行,我们才去读下一行。在这里我们只设置了一行。Mp3 mp3Info=(Mp3)iterator.next();//感觉这样一打印,从打印的方式可以看出就是直接用了Mp3Info里面的toString()的方法。肯定也是因为mp3Info不是String类型的,所以打印前会自动调用toStringSystem.out.println(mp3Info);}} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}return infos;}//5、Mp3ListContentHandler类,就是xml文件的具体解析的过程。package xml;import java.util.List;import com.music.mp3.Mp3;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;//这部分属于xml文件解析的内容,ContentHandler 是java类包中一个特殊的sax接口,该接口已经封装好了一些对事件响应的方法//当xml解析器开始解析xml输入文档时,它会遇到某些特殊的事件,包括文档的开头和结束,元素的开头和结束,以及元素的中字符数据事件//等事件,当遇到这些事件时,xml解析器就会调用ContentHandler接口中相应的方法来响应事件。defaultHandler已经实现了一些不常用的方法//是ContentHandler的子类,所以我们只用继承DefaultHandler就可以了public class Mp3ListContentHandler extends DefaultHandler{private List<Mp3> infos=null; public Mp3ListContentHandler(List<Mp3> infos) {super();this.infos = infos;}public List<Mp3> getInfos() {return infos;}public void setInfos(List<Mp3> infos) {this.infos = infos;}private Mp3 mp3Info=null;private String tagName=null;@Overridepublic void characters(char[] ch, int start, int length)//当每调用startElement之后将会调用这一段函数throws SAXException {String temp=new String(ch,start,length);if(tagName.equals("id"))//请仔细观察,看看这个tagname{mp3Info.setId(Integer.parseInt(temp));}else if(tagName.equals("mp3.name")){mp3Info.setMp3_name(temp);}else if(tagName.equals("mp3.size")){mp3Info.setMp3_size(temp);}else if(tagName.equals("lrc.name")){mp3Info.setLrc_name(temp);}else if(tagName.equals("lrc.size")){mp3Info.setLrc_size(temp);}}@Overridepublic void endDocument() throws SAXException {//当读完xml文档的时候就会调用这个方法// TODO Auto-generated method stub}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {// TODO Auto-generated method stubif(qName.equals("resource")){infos.add(mp3Info);//将一个mp3对象加入到集合List中}tagName="";}@Overridepublic void startDocument() throws SAXException {//当开始读xml文档的时候就会调用这个方法// TODO Auto-generated method stubsuper.startDocument();}@Override//当读到一个resources的时候就会调用这个方法public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {// TODO Auto-generated method stubthis.tagName=localName;//是指不带前缀的值,qName是带前缀的。if(tagName.equals("resource"))//当读到resource表示一个资源的开始,所以我们新建了一个mp3Info的对象{mp3Info=new Mp3();}}}@Overridepublic void endDocument() throws SAXException {// TODO Auto-generated method stub}@Overridepublic void endElement(String uri, String localName, String qName)throws SAXException {// TODO Auto-generated method stubif(qName.equals("resource")){infos.add(mp3Info);//将一个mp3对象加入到集合List中}tagName="";}@Overridepublic void startDocument() throws SAXException {// TODO Auto-generated method stubsuper.startDocument();}@Overridepublic void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {// TODO Auto-generated method stubthis.tagName=localName;//是指不带前缀的值,qName是带前缀的。if(tagName.equals("resource"))//当读到resource表示一个资源的开始,所以我们新建了一个mp3Info的对象{mp3Info=new Mp3();}}}
?解析完了之后,将会放在一个List<Mp3> mp3Infos的集合中,最后显示出来就好了。
?
?
让我们看两张图片
第一张:读服务器端下载了的xml文件
?
第二张:解析xml文件后打印出来的
?
我们可以看到解析过程是非常成功的。
?
?
?
最后,我想对ContentHandler的子类DefaultHandler做一点说明,这个类是专门用于解析xml文件的,DefaultHandler已经实现了很多复杂的,我们不常用的解析xml文件的方法。继承DefaultHandler这个类的时候必须实现5个方法:
public void startDocument () { ?
? ? ? ? //开始解析文档时调用
? ? } ?
??
? ? public void endDocument () { ?
? ? ? ? //文档解析结束时调用 ?
? ? } ?
??
? ? public void startElement (String uri, String localName, String qName, Attributes attributes) { ?
? ? ? ? //开始解析一个元素时调用,过后立即调用 characters()
? ? } ?
? ? ??
? ? public void characters (char[] ch, int start, int length) { ?
? ? ? ? ??
? ? } ?
? ? ??
? ? public void endElement (String uri, String localName, String qName) { ?
? ? ? ? //结束解析节一个元素的时候就调用。?
? ? } ?
?
比如说,当我们这个xml文档开始时就会调用startDocument () ,之后读到<resources>算一个元素的开始,还是会调用startElement (),接着调用characters(),但是我们在代码没有处理,所以这个读了也等于没读。接着<resource>,<id>都算是一个元素的开始。</id>、</resource>就算是一个元素的结束。综合以上,就会实现我们想要的效果了。
?
呼。。。。加油啊
?
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?------- android培训、java培训、期待与您交流! ----------