調用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 |