3.4 網絡存儲數據

調用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接口和類

  • Document:該接口定義分析並建立DOM文檔的一系列方法,它是文檔樹的根,是操做DOM的基礎。 
  • Element:該接口繼承Node接口,提供了獲取、修改XML元素名字和屬性的方法。
  • Node:該接口提供處理並獲取節點和子節點值的方法。
  • NodeList:提供得到節點個數和當前節點的方法。這樣就能夠迭代地訪問各個節點。
  • DOMParser:該類是Apache的Xerces中的DOM解析器類,可直接解析XML文件。

第二種方式: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接口和類:

  • Attrbutes:用於獲得屬性的個數、名字和值。 
  • ContentHandler:定義與文檔自己關聯的事件(例如,開始和結束標記)。大多數應用程序都註冊這些事件。
  • DTDHandler:定義與DTD關聯的事件,但並不完整。要對DTD進行語法分析,請使用可選的DeclHandler。
  • DeclHandler是SAX的擴展。不是全部的語法分析器都支持它。
  • EntityResolver:定義與裝入實體關聯的事件。只有少數幾個應用程序註冊這些事件。
  • ErrorHandler:定義錯誤事件。許多應用程序註冊這些事件以便用它們本身的方式報錯。
  • DefaultHandler:它提供了這些接口的缺省實現。在大多數狀況下,爲應用程序擴展DefaultHandler並覆蓋相關的方法要比直接實現一個接口更容易。

第三種方式:PULL解析器:

  Android並未提供對Java StAX API的支持。可是,Android附帶了一個pull解析器,其工做方式相似於StAX。它容許用戶的應用程序代碼從解析器中獲取事件,這與SAX解析器自動將事件推入處理程序相反。

  PULL解析器的運行方式和SAX相似,基於事件的模式。不一樣的是,在PULL解析過程當中返回的是數字,且咱們須要本身獲取產生的事件而後作相應的操做,而不像SAX那樣由處理器觸發一種事件的方法,執行咱們的代碼。

  • 讀取到xml的聲明返回             START_DOCUMENT;
  • 讀取到xml的結束返回             END_DOCUMENT ;
  • 讀取到xml的開始標籤返回     START_TAG                  調用XmlPullParser.next()循環讀取節點
  • 讀取到xml的結束標籤返回     END_TA                        switch處理
  • 讀取到xml的文本返回             TEXT

  PULL解析器小巧輕便,解析速度快,簡單易用,很是適合在Android移動設備中使用,Android系統內部在解析各類XML時也是用PULL解析器,Android官方推薦開發者們使用Pull解析技術。Pull解析技術是第三方開發的開源技術,它一樣能夠應用於JavaSE開發。

  PULL 的工做原理:XML pull提供了開始元素和結束元素。當某個元素開始時,咱們能夠調用parser.nextText從XML文檔中提取全部字符數據。當解釋到一個文檔結束時,自動生成EndDocument事件。

經常使用的XML pull的接口和類:

  • XmlPullParser:XML pull解析器是一個在XMLPULL VlAP1中提供了定義解析功能的接口。
  • XmlSerializer:它是一個接口,定義了XML信息集的序列。
  • XmlPullParserFactory:這個類用於在XMPULL V1 API中建立XML Pull解析器。
  • XmlPullParserException:拋出單一的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;
    }
}

XMLJSON的區別:

  • XML的主要組成成分是element、attribute和element content。
  • JSON的主要組成成分是object、array、string、number、boolean(true/false)和null。
  • JSON相比XML的不一樣之處:沒有結束標籤,更短,讀寫的速度更快,可以使用內建的 JavaScript eval() 方法進行解析,使用數組,不使用保留字。總之: JSON 比 XML 更小、更快,更易解析。
  • XML須要選擇怎麼處理element content的換行,而JSON string則不須做這個選擇。
  • XML只有文字,沒有預設的數字格式,而JSON則有明確的number格式,這樣在locale上也安全。
  • XML映射數組沒大問題,就是數組元素tag比較重複冗餘。JSON 比較易讀。
  • JSON的true/false/null也能容易統一至通常編程語言的對應語義。

解析XMLSAX能夠快速掃描一個大型的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特色

  • 對序列化數據的訪問不須要打包和拆包——它將序列化數據存儲在緩存中,這些數據既能夠存儲在文件中,又能夠經過網絡原樣傳輸,而沒有任何解析開銷;
  • 內存效率和速度——訪問數據時的惟一內存需求就是緩衝區,不須要額外的內存分配。
  • 擴展性、靈活性——它支持的可選字段意味着不只能得到很好的前向/後向兼容性(對於長生命週期的遊戲來講尤爲重要,由於不須要每一個新版本都更新全部數據);
  • 最小代碼依賴——僅僅須要自動生成的少許代碼和一個單一的頭文件依賴,很容易集成到現有系統中。
  • 強類型設計——儘量使錯誤出如今編譯期,而不是等到運行期才手動檢查和修正;
  • 使用簡單——生成的C++代碼提供了簡單的訪問和構造接口;並且若是須要,經過一個可選功能能夠用來在運行時高效解析Schema和類JSON格式的文本;
  • 跨平臺——支持C++、Java,而不須要任何依賴庫;在最新的gcc、clang、vs2010等編譯器上工做良好;

3 FlatBuffersProtocol 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

相關文章
相關標籤/搜索