调用WebService返回的数据或是解析HTTP协议(见5.1节)实现网络数据交互。html
存储格式通常为XML或是JSON。本节主要讲解这两种类型的数据解析。java
XML被设计用来结构化、存储以及传输信息。node
JSON:JavaScript对象表示法(JavaScript Object Notation), 是一种轻量级的数据交换格式, 易于人阅读和编写, 同时也易于机器解析和生成。android
3.4.1 解析XMLgit
常见的XML解析器分别为DOM解析器、SAX解析器和PULL解析器。另,对于频繁用XML交互的话可使用xStream框架,一个简单的工具包,用来把对象序列化成xml配置文件,而且也能够把xml反序化成对象。github
第一种方式:DOM解析器:编程
DOM是基于树形结构的的节点或信息片断的集合,容许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构一般须要加载整个文档和构造树形结构,而后才能够检索和更新节点信息。json
Android彻底支持DOM 解析。利用DOM中的对象,能够对XML文档进行读取、搜索、修改、添加和删除等操做。api
DOM的工做原理:使用DOM对XML文件进行操做时,首先要解析文件,将文件分为独立的元素、属性和注释等,而后以节点树的形式在内存中对XML文件进行表示,就能够经过节点树访问文档的内容,并根据须要修改文档——这就是DOM的工做原理。数组
DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,而后构造一个驻留内存的树结构,这样代码就可使用DOM接口来操做整个树结构。
优缺点:因为DOM在内存中以树形结构存放,所以检索和更新效率会更高。可是对于特别大的文档,解析和加载整个文档将会很耗资源。 固然,若是XML文件的内容比较小,采用DOM是可行的。
经常使用的DoM接口和类:
第二种方式:SAX解析器:
Simple API for XML解析器是基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工做的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就能够获得处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才可以根据提供的事件信息来决定本身的行为。
SAX解析器的优势是解析速度快,占用内存少。很是适合在Android移动设备中使用。
SAX的工做原理:SAX的工做原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数作相应动做,而后继续一样的扫描,直至文档结束。
在SAX中,事件源是org.xml.sax包中的XMLReader,它经过parser()方法来解析XML文档,并产生事件。
事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader经过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的链接。(可知,咱们须要XmlReader 以及DefaultHandler来配合解析xml。)
经常使用的SAX接口和类:
第三种方式:PULL解析器:
Android并未提供对Java StAX API的支持。可是,Android附带了一个pull解析器,其工做方式相似于StAX。它容许用户的应用程序代码从解析器中获取事件,这与SAX解析器自动将事件推入处理程序相反。
PULL解析器的运行方式和SAX相似,基于事件的模式。不一样的是,在PULL解析过程当中返回的是数字,且咱们须要本身获取产生的事件而后作相应的操做,而不像SAX那样由处理器触发一种事件的方法,执行咱们的代码。
PULL解析器小巧轻便,解析速度快,简单易用,很是适合在Android移动设备中使用,Android系统内部在解析各类XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它一样能够应用于JavaSE开发。
PULL 的工做原理:XML pull提供了开始元素和结束元素。当某个元素开始时,咱们能够调用parser.nextText从XML文档中提取全部字符数据。当解释到一个文档结束时,自动生成EndDocument事件。
经常使用的XML pull的接口和类:
[附加]第四种方式:Android.util.Xml类
在Android API中,另外提供了Android.util.Xml类,一样能够解析XML文件,使用方法相似SAX,也都需编写Handler来处理XML的解析,可是在使用上却比SAX来得简单 ,以下所示:
MyHandler myHandler=new MyHandler(); Android.util.Xml.parse(ur1.openConnection().getlnputStream(), Xml.Encoding.UTF-8, myHandler);
几种解析技术的比较与总结:
对于Android的移动设备而言,由于设备的资源比较宝贵,内存是有限的,因此咱们须要选择适合的技术来解析XML,这样有利于提升访问的速度。
1) DOM在处理XML文件时,将XML文件解析成树状结构并放入内存中进行处理。当XML文件较小时,咱们能够选DOM,由于它简单、直观。
2) SAX则是以事件做为解析XML文件的模式,它将XML文件转化成一系列的事件,由不一样的事件处理器来决定如何处理。XML文件较大时,选择SAX技术是比较合理的。虽然代码量有些大,可是它不须要将全部的XML文件加载到内存中。这样对于有限的Android内存更有效,并且Android提供了一种传统的SAX使用方法以及一个便捷的SAX包装器。 使用Android.Util.Xml类,从示例中能够看出,会比使用 SAX来得简单。
3) XML pull解析并未像SAX解析那样监听元素的结束,而是在开始处完成了大部分处理。这有利于提前读取XML文件,能够极大的减小解析时间,这种优化对于链接速度较漫的移动设备而言尤其重要。对于XML文档较大但只须要文档的一部分时,XML Pull解析器则是更为有效的方法。
Dom 解析 import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.leo.sax_parser.model.Person; public class DomParser { public static List<Person> readXMLByDom(InputStream input) throws ParserConfigurationException, SAXException, IOException { List<Person> persons = new ArrayList<Person>(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(input); Element root = document.getDocumentElement(); NodeList nodes = root.getElementsByTagName("person"); for (int i = 0; i < nodes.getLength(); i++) { //person对象个数 Element element = (Element) nodes.item(i); Person person = new Person(); person.setId(element.getAttribute("id")); NodeList childNodes = element.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { // 对象中属性的个数:id,name,age,phoneNumber Node child = childNodes.item(j); // 解决getChildNodes().getLength()与实际不符的问题 if (child.getNodeType() != Node.ELEMENT_NODE) { continue; } Element childElement = (Element) child; Log.i("DomParser", childElement.getNodeName() + ":" + childElement.getTextContent().trim()); if ("name".equals(childElement.getNodeName())) { person.setName(childElement.getTextContent().trim()); } else if ("age".equals(childElement.getNodeName())) { person.setAge(Integer.parseInt(childElement.getTextContent().trim())); } else if ("phoneNumber".equals(childElement.getNodeName())) { person.setPhoneNumber(Integer.parseInt(childElement.getFirstChild().getNodeValue())); } } persons.add(person); } return persons; } }
[代码]SAX 解析 import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class SAX_handler extends DefaultHandler { private List<Person> persons; private String perTag; private Person person; public List<Person> getPersons() { return persons; } @Override public void startDocument() throws SAXException { persons = new ArrayList<Person>(); //初始化用于存放person对象的persons,用于存放读取到的相应的信息。 } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if ("person".equals(localName)) { for (int i = 0; i < attributes.getLength(); i++) { Log.i(Tag, "attributeName:" + attributes.getLocalName(i) + "attributeValue:" + attributes.getValue(i)); person = new Person(); person.setId(attributes.getValue(i)); } } perTag = localName; } @Override public void characters(char[] ch, int start, int length) throws SAXException { String data = new String(ch, start, length); if (!"".equals(data.trim())) Log.i(Tag, "Content:" + data); if ("name".equals(perTag)) { person.setName(data); } else if ("age".equals(perTag)) { person.setAge(Integer.parseInt(data)); } else if ("phoneNumber".equals(perTag)) { person.setPhoneNumber(Integer.parseInt(data)); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (person != null&&"person".equals(localName)) { persons.add(person); person = null; } perTag = null; } @Override public void endDocument() throws SAXException { Log.i(Tag, "endDocument"); } }
Pull 解析 import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.util.Xml; public class PullParser { public static List<Person> readXML(InputStream inputstream) throws XmlPullParserException, IOException { List<Person> persons = null; XmlPullParser parser = Xml.newPullParser(); parser.setInput(inputstream, "UTF-8"); int eventCode = parser.getEventType(); Person person = null; while (eventCode != XmlPullParser.END_DOCUMENT) { switch (eventCode) { case XmlPullParser.START_DOCUMENT: persons = new ArrayList<Person>(); break; case XmlPullParser.START_TAG: if ("person".equals(parser.getName())) { person = new Person(); person.setId(parser.getAttributeValue(0)); }else if(person != null){ if ("name".equals(parser.getName())) { person.setName(parser.nextText()); }else if("age".equals(parser.getName())){ person.setAge(Integer.parseInt(parser.nextText())); }else if("phoneNumber".equals(parser.getName())){ person.setPhoneNumber(Integer.parseInt(parser.nextText())); } } break; case XmlPullParser.END_TAG: if ("person".equals(parser.getName()) && person!= null) { persons.add(person); person =null; } break; default: break; } eventCode = parser.next(); } return persons; } }
XML和JSON的区别:
解析XML:SAX能够快速扫描一个大型的XML文档,当它找到查询标准时就会当即中止,而后再处理之。DOM是把XML所有加载到内存中创建一棵树以后再进行处理。因此DOM不适合处理大型的XML【会产生内存的急剧膨胀】。
解析JSON:Android自带了JSON解析的相关API(org.json)。谷歌的Gson,阿里的FastJson,还有一个jackJson。有人说jackJson解析速度快,大数据时FastJson要比Gson效率高,小数据时反之。
3.4.2 解析Json
A、服务器端将数据转换成json字符串
首先、服务器端项目要导入Gson的jar包到BuiltPath中。而后将数据转为json字符串,核心函数是:
public static String createJsonString(Object value){ Gson gson = new Gson(); String str = gson.toJson(value); return str; }
B、客户端将json字符串转换为相应的javaBean
首先客户端也要导入gson的两个jar包,json的jar就不须要导入了(由于android已经集成了json的jar包)
一、客户端获取json字符串
public class HttpUtil{ public static String getJsonContent(String urlStr){ try{// 获取HttpURLConnection链接对象 URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(3000); conn.setDoInput(true); conn.setRequestMethod("GET"); int respCode = httpConn.getResponseCode();// 获取相应码 if (respCode == 200) return ConvertStream2Json(httpConn.getInputStream()); } catch (IOException e){ e.printStackTrace(); } return ""; } private static String ConvertStream2Json(InputStream inputStream){ String jsonStr = ""; ByteArrayOutputStream out = new ByteArrayOutputStream();//至关于内存输出流 byte[] buffer = new byte[1024]; int len = 0; try{// 将输入流转移到内存输出流中 while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) out.write(buffer, 0, len); jsonStr = new String(out.toByteArray());// 将内存流转换为字符串 } catch (IOException e){ e.printStackTrace(); } return jsonStr; } }
二、使用泛型获取javaBean(核心函数)
public static <T> T getPerson(String jsonString, Class<T> cls) { T t = null; try { Gson gson = new Gson(); t = gson.fromJson(jsonString, cls); } catch (Exception e) { } return t; } public static <T> List<T> getPersons(String jsonString, Class<T> cls) { List<T> list = new ArrayList<T>(); try { Gson gson = new Gson(); list = gson.fromJson(jsonString, new TypeToken<List<cls>>() { }.getType()); } catch (Exception e) { } return list; } public static List<Map<String, Object>> listKeyMaps(String jsonString) { List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); try { Gson gson = new Gson(); list = gson.fromJson(jsonString, new TypeToken<List<Map<String, Object>>>() { }.getType()); } catch (Exception e) { } return list; }
3.4.3 Protocol Buffers
1概述
ProtocolBuffer是用于结构化数据串行化的灵活、高效、自动的方法,有如XML,不过它更小、更快、也更简单。你能够定义本身的数据结构,而后使用代码生成器生成的代码来读写这个数据结构。你甚至能够在无需从新部署程序的状况下更新数据结构。目前仅提供了 C++、Java、Python 三种语言的 API。
2工做原理
你首先须要在一个 .proto 文件中定义你须要作串行化的数据结构信息。每一个ProtocolBuffer信息是一小段逻辑记录,包含一系列的键值对。
一旦你定义了本身的报文格式(message),你就能够运行ProtocolBuffer编译器,将你的 .proto 文件编译成特定语言的类。这些类提供了简单的方法访问每一个字段(像是 query() 和 set_query() ),像是访问类的方法同样将结构串行化或反串行化。
你能够在不影响向后兼容的状况下随意给数据结构增长字段,旧有的数据会忽略新的字段。因此若是使用ProtocolBuffer做为通讯协议,你能够无须担忧破坏现有代码的状况下扩展协议。
3优缺点
ProtocolBuffer拥有多项比XML更高级的串行化结构数据的特性:
更简单,小3-10倍,快20-100倍,更少的歧义,能够方便的生成数据存取类
固然,ProtocolBuffer并非在任什么时候候都比XML更合适,例如ProtocolBuffer没法对一个基于标记文本的文档建模,由于你根本无法方便的在文本中插入结构。另外,XML是便于人类阅读和编辑的,而ProtocolBuffer则不是。还有XML是自解释的,而 ProtocolBuffer仅在你拥有报文格式定义的 .proto 文件时才有意义。
4使用方法
下载包( http://code.google.com/p/protobuf/downloads/ ),包含了Java、Python、C++的ProtocolBuffer编译器,用于生成你须要的IO类。构建和安装你的编译器,跟随README的指令就能够作到。
一旦你安装好了,就能够跟着编程指导( http://code.google.com/apis/protocolbuffers/docs/tutorials.html )来选择语言-随后就是使用ProtocolBuffer建立一个简单的应用了。
1) 所需文件:proto.exe, protobuf-java-2.4.1.jar
2) 创建一个工程TestPb,在下面创建一个proto文件,用来存放【.proto】文件
3) 将proto.exe放在工程目录下
4) 创建一个msg.proto文件:cmd 打开命令工具,输入命令:protoc --java_out=./ msg.proto
5) 再次进入目录后会发现该目录多了一个文件夹,即以该proto的package命名的的目录,会产生一个Msg.java的文件,这时这个文件就可使用到咱们的java或者 android 工程了。
6) 导如jar包到工程中,就可使用protobuf了
7) Protobuf 实现Socket通讯
3.4.4 FlatBuffers
1概述
FlatBuffers是一个开源的、跨平台的、高效的、提供了C++/Java接口的序列化工具库。它是Google专门为游戏开发或其余性能敏感的应用程序需求而建立。尤为更适用于移动平台,这些平台上内存大小及带宽相比桌面系统都是受限的,而应用程序好比游戏又有更高的性能要求。它将序列化数据存储在缓存中,这些数据既能够存储在文件中,又能够经过网络原样传输,而不须要任何解析开销。
2特色
3 FlatBuffers和Protocol Buffers以及Json的比较:
FlatBuffers的功能和Protocol Buffers很像,他们的最大不一样点是在使用具体的数据以前,FlatBuffers不须要解析/解包的过程。同时, FlatBuffers的引用比Protocol Buffers方便不少,只须要包含两三个头文件便可
JSON做为数据交换格式,被普遍用户各类动态语言之间(固然也包括静态语言)。它的优势是可读性好,同时它的最大的缺点那就是解析时的性能问题了。并且由于它的动态类型特色,你的代码可能还须要多写好多类型、数据检查逻辑。
在作 Android 开发的时候,JSON 是最经常使用的数据序列化技术。咱们知道,JSON 的可读性很强,可是序列化和反序列化性能倒是最差的。解析的时候,JSON 解析器首先,须要在内存中初始化一个对应的数据结构,这个事件常常会消耗 100ms ~ 200ms2;解析过程当中,要产生大量的临时变量,形成 Java 虚拟机的 GC 和内存抖动,解析 20KB 的数据,大概会消耗 100KB 的临时内存2。FlatBuffers 就解决了这些问题。
4使用方法
1) 编写一个用来定义你想序列化的数据的schema文件(又称IDL),数据类型能够是各类大小的int、float,或者是string、array,或者另外一对象的引用,甚至是对象集合;
2) 各个数据属性都是可选的,且能够设置默认值。
3) 使用FlatBuffer编译器flatc生成C++头文件或者Java类,生成的代码里额外提供了访问、构造序列化数据的辅助类。生成的代码仅仅依赖flatbuffers.h;
4) 使用FlatBufferBuilder类构造一个二进制buffer。你能够向这个buffer里循环添加各类对象,并且很简单,就是一个单一函数调用;
5) 保存或者发送该buffer
6) 当再次读取该buffer时,你能够获得这个buffer根对象的指针,而后就能够简单的就地读取数据内容;
简单来讲,FlatBuffers 的使用方法是,首先按照使用特定的 IDL 定义数据结构 schema,而后使用编译工具 flatc 编译 schema 生成对应的代码,把生成的代码应用到工程中便可。
首先,咱们须要获得 flatc,这个须要从源码编辑获得。从 GitHub 上 Clone 代码,
$ git clone https://github.com/google/flatbuffers |
在 Mac 上,使用 Xcode 直接打开 build/Xcode/ 里面项目文件,编译运行,便可在项目根目录生成咱们须要的 flatc 工具。也可使用 cmake 编辑,例如在 Linux 上,运行以下命令便可:
$ cmake -G "Unix Makefiles" $ make |