PULL解析器,因?yàn)镾AX解析器操作起來太笨重,DOM不適合文檔較大,內(nèi)存較小的場(chǎng)景,唯有PULL輕巧靈活,速度快,占用內(nèi)存小,使用非常順手。
貴州ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書未來市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!
在Android中,常見的XML解析器分別為SAX解析器、DOM解析器和PULL解析器,下面一一詳細(xì)介紹。
1、SAX解析器:
SAX(Simple API for XML)解析器是一種基于事件的解析器,它的核心是事件處理模式,主要是圍繞著事件源以及事件處理器來工作的。當(dāng)事件源產(chǎn)生事件后,調(diào)用事件處理器相應(yīng)的處理方法,一個(gè)事件就可以得到處理。在事件源調(diào)用事件處理器中特定方法的時(shí)候,還要傳遞給事件處理器相應(yīng)事件的狀態(tài)信息,這樣事件處理器才能夠根據(jù)提供的事件信息來決定自己的行為。
SAX解析器的優(yōu)點(diǎn)是解析速度快,占用內(nèi)存少。非常適合在Android移動(dòng)設(shè)備中使用。
2、DOM解析器:
DOM是基于樹形結(jié)構(gòu)的的節(jié)點(diǎn)或信息片段的集合,允許開發(fā)人員使用DOM API遍歷XML樹、檢索所需數(shù)據(jù)。分析該結(jié)構(gòu)通常需要加載整個(gè)文檔和構(gòu)造樹形結(jié)構(gòu),然后才可以檢索和更新節(jié)點(diǎn)信息。
由于DOM在內(nèi)存中以樹形結(jié)構(gòu)存放,因此檢索和更新效率會(huì)更高。但是對(duì)于特別大的文檔,解析和加載整個(gè)文檔將會(huì)很耗資源。
3、PULL解析器:
PULL解析器的運(yùn)行方式和SAX類似,都是基于事件的模式。不同的是,在PULL解析過程中,我們需要自己獲取產(chǎn)生的事件然后做相應(yīng)的操作,而不像SAX那樣由處理器觸發(fā)一種事件的方法,執(zhí)行我們的代碼。PULL解析器小巧輕便,解析速度快,簡(jiǎn)單易用,非常適合在Android移動(dòng)設(shè)備中使用,Android系統(tǒng)內(nèi)部在解析各種XML時(shí)也是用PULL解析器。
在3096行調(diào)用scanDirTracedLI掃描app目錄
sAppInstallDir systemAppDir
它有兩個(gè)重要的地方
它將輕量級(jí)解析單個(gè)包的詳細(xì)信息。PackageParser.PackageLite
在123行 循環(huán)遍歷文件夾判斷是否是apk文件,是的話使用parseApkLite()函數(shù)進(jìn)行解析,解析完了后,通過獲取一些屬性拼裝new PackageParser.PackageLite返回
在parseApkLiteInner()函數(shù)中通過獲取Mainifest.xml的XmlResourceParser,最終調(diào)用了parseApkLite(input, apkPath, parser, attrs, signingDetails)去解析, 他通過一些標(biāo)簽獲取一些屬性,最終拼裝成PackageParser.ApkLite 返回
ParsingPackageUtils.java
parseBaseApk(5個(gè)參數(shù)) -- 372行 parseBaseApk(6個(gè)參數(shù)) -- parseBaseApkTags() --- parseBaseApplication()(后面還有parseBaseApkTag()函數(shù))
就是解析xml,簡(jiǎn)單看看就行了~
#######PackageManagerService.scanDirLI()
這里我們就不用看這個(gè)方法了,通過描述得知
android中xml解析pull與sax,可以通過以下介紹了解區(qū)別:
1、sax是一個(gè)解析速度快并且占用內(nèi)存少的xml解析器,非常適合用于android等移動(dòng)設(shè)備,sax全稱是simple
api
for
xml,既是指一種接口,也是一個(gè)軟件包,作為接口,sax是事件驅(qū)動(dòng)型xml解析的一個(gè)標(biāo)準(zhǔn)接口。具有如下特點(diǎn)1.
解析效率高,占用內(nèi)存少。2.可以隨時(shí)停止解析。3.不能載入整個(gè)文檔到內(nèi)存。4.不能寫入xml5.sax解析xml文件采用的是事件驅(qū)動(dòng)。
2、pull解析xml文件的方式與sax解析xml文件的方式大致相同,他們都是基于事件驅(qū)動(dòng)的,頁是android中默認(rèn)的解析方式,更適用于移動(dòng)平臺(tái)的解析方式。所以,利用pull解析xml文件需要下面幾個(gè)步驟:1.通過xmlpullparserfactory獲取xmlpullparser對(duì)象。2.通過xmlpullparser對(duì)象設(shè)置輸入流。3.通過parser.next(),持續(xù)的解析xml文件直到文件的尾部。
本文主要講解Android開發(fā)中如何對(duì)XML文件的解析,由于XML文件具有與平臺(tái)無關(guān),廣泛應(yīng)用于數(shù)據(jù)通信中,因此解析XML文件就顯得很有意義。Android對(duì)XML文件解析的方法主要有3種。 通常有三種方式:DOM、SAX和PULL,下面就分別針對(duì)這三種方式來進(jìn)行討論。
文件內(nèi)容如下所示:
那么就是要對(duì)此XML文件做解析。下面我們就分別用DOM,SAX和PULL三種方式,分別對(duì)此XML文件做解析。
DOM方式
DOM方式解析xml是先把xml文檔都讀到內(nèi)存中,然后再用DOM API來訪問樹形結(jié)構(gòu),并獲取數(shù)據(jù)。由DOM解析的方式可以知道,如果XML文件很大的時(shí)候,處理效率就會(huì)變得比較低,這也是DOM方式的一個(gè)缺點(diǎn)。
現(xiàn)在我們來解析上文中提到的有關(guān)天氣預(yù)報(bào)信息相關(guān)的xml文件。什么是解析呢?說的通俗一點(diǎn),就是將這個(gè)帶標(biāo)簽的XML文件識(shí)別出來,并抽取一些相關(guān)的,對(duì)我們有用的信息來給我們使用。那在這個(gè)文件里,時(shí)間,天氣,溫度,以及圖標(biāo)對(duì)我們來說是需要得到的。我們要對(duì)其做解析。
解析的具體思路是:
1. 將XML文件加載進(jìn)來。
2. 獲取文檔的根節(jié)點(diǎn)
3. 獲取文檔根節(jié)點(diǎn)中所有子節(jié)點(diǎn)的列表
4. 獲取子節(jié)點(diǎn)列表中需要讀取的節(jié)點(diǎn)信息
根據(jù)這4個(gè)步驟,我們進(jìn)行開發(fā):
首先就是如何加載XML文件,假設(shè)此文件來源于網(wǎng)絡(luò)。
SAX方式
SAX是Simple API for XML的縮寫。是一個(gè)包也可以看成是一些接口。
相比于DOM而言SAX是一種速度更快,更有效,占用內(nèi)存更少的解析XML文件的方法。它是逐行掃描,可以做到邊掃描邊解析,因此SAX可以在解析文檔的任意時(shí)刻停止解析。非常適用于Android等移動(dòng)設(shè)備。
SAX是基于事件驅(qū)動(dòng)的。所謂事件驅(qū)動(dòng)就是說,它不用解析完整個(gè)文檔,在按內(nèi)容順序解析文檔過程中,SAX會(huì)判斷當(dāng)前讀到的字符是否符合XML文件語法中的某部分。如果符合某部分,則會(huì)觸發(fā)事件。所謂觸發(fā)事件,就是調(diào)用一些回調(diào)方法。當(dāng)然android的事件機(jī)制是基于回調(diào)方法的,在用SAX解析xml文檔時(shí)候,在讀取到文檔開始和結(jié)束標(biāo)簽時(shí)候就會(huì)回調(diào)一個(gè)事件,在讀取到其他節(jié)點(diǎn)與內(nèi)容時(shí)候也會(huì)回調(diào)一個(gè)事件。在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通過parser()方法來解析XML文檔,并產(chǎn)生事件。事件處理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver這4個(gè)接口。
這四個(gè)接口的詳細(xì)說明如下:
事件處理器名稱
事件處理器處理的事件
XMLReader注冊(cè)方法
ContentHander
XML文檔的開始與結(jié)束,
XML文檔標(biāo)簽的開始與結(jié)束,接收字符數(shù)據(jù),跳過實(shí)體,接收元素內(nèi)容中可忽略的空白等。
setContentHandler(ContentHandler h)
DTDHander
處理DTD解析時(shí)產(chǎn)生的相應(yīng)事件
setDTDHandler(DTDHandler h)
ErrorHandler
處理XML文檔時(shí)產(chǎn)生的錯(cuò)誤
setErrorHandler(ErrorHandler h)
EntityResolver
處理外部實(shí)體
setEntityResolver(EntityResolver e)
我們用來做內(nèi)容解析的回調(diào)方法一般都定義在ContentHandler接口中。
ContentHandler接口常用的方法:
startDocument()
當(dāng)遇到文檔的開頭的時(shí)候,調(diào)用這個(gè)方法,可以在其中做一些預(yù)處理的工作。
endDocument()
當(dāng)文檔結(jié)束的時(shí)候,調(diào)用這個(gè)方法,可以在其中做一些善后的工作。
startElement(String namespaceURI, String localName,String qName, Attributes atts)
當(dāng)讀到開始標(biāo)簽的時(shí)候,會(huì)調(diào)用這個(gè)方法。namespaceURI就是命名空間,localName是不帶命名空間前綴的標(biāo)簽名,qName是帶命名空間前綴的標(biāo)簽名。通過atts可以得到所有的屬性名和相應(yīng)的值。
endElement(String uri, String localName, String name)
在遇到結(jié)束標(biāo)簽的時(shí)候,調(diào)用這個(gè)方法。
characters(char[] ch, int start, int length)
這個(gè)方法用來處理在XML文件中讀到的內(nèi)容。例如:high data="30"/主要目的是獲取high標(biāo)簽中的值。
第一個(gè)參數(shù)用于存放文件的內(nèi)容,后面兩個(gè)參數(shù)是讀到的字符串在這個(gè)數(shù)組中的起始位置和長(zhǎng)度,使用new String(ch,start,length)就可以獲取內(nèi)容。
注意:
SAX的一個(gè)重要特點(diǎn)就是它的流式處理,當(dāng)遇到一個(gè)標(biāo)簽的時(shí)候,它并不會(huì)紀(jì)錄下之前所碰到的標(biāo)簽,即在startElement()方法中,所有能夠知道的信息,就是標(biāo)簽的名字和屬性,至于標(biāo)簽的嵌套結(jié)構(gòu),上層標(biāo)簽的名字,是否有子元屬等等其它與結(jié)構(gòu)相關(guān)的信息,都是不知道的,都需要你的程序來完成。這使得SAX在編程處理上沒有DOM方便。
現(xiàn)在我們截取一段XML文件來做解析,其調(diào)用方法是這樣的:
?xml version="1.0"? ---------- startDocument()
weather ---------- startElement
forecast_information ---------- startElement
city ---------- startElement
beijing ---------- characters
/city ---------- endElement
/forecast_information ---------- endElement
/weather ---------- endElement
文檔結(jié)束 ---------- endDocument()
SAX的解析步驟:
首先需要注意的是:
SAX還為其制定了一個(gè)Helper類:DefaultHandler它實(shí)現(xiàn)了ContentHandler這個(gè)接口,但是其所有的方法體都為空,在實(shí)現(xiàn)的時(shí)候,你只需要繼承這個(gè)類,然后重載相應(yīng)的方法即可。
使用SAX解析XML文件一般有以下五個(gè)步驟:
1、創(chuàng)建一個(gè)SAXParserFactory對(duì)象;
2、調(diào)用SAXParserFactory中的newSAXParser方法創(chuàng)建一個(gè)SAXParser對(duì)象;
3、然后在調(diào)用SAXParser中的getXMLReader方法獲取一個(gè)XMLReader對(duì)象;
4、實(shí)例化一個(gè)DefaultHandler對(duì)象
5、連接事件源對(duì)象XMLReader到事件處理類DefaultHandler中
6、調(diào)用XMLReader的parse方法從輸入源中獲取到的xml數(shù)據(jù)
7、通過DefaultHandler返回我們需要的數(shù)據(jù)集合。
我們?nèi)匀粊斫馕錾鲜瞿莻€(gè)天氣預(yù)報(bào)的XML文件。
編寫代碼如下:
[java] view plaincopy
mySAX.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
try{
String url = ";weather=beijing";
DefaultHttpClient client = new DefaultHttpClient();
HttpUriRequest req = new HttpGet(url);
HttpResponse resp = client.execute(req);
HttpEntity ent = resp.getEntity();
InputStream stream = ent.getContent(); //將文件導(dǎo)入流,因此用InputStream
SAXParserFactory saxFactory = SAXParserFactory.newInstance(); //獲取一個(gè)對(duì)象
SAXParser saxParser = saxFactory.newSAXParser();//利用獲取到的對(duì)象創(chuàng)建一個(gè)解析器
XMLContentHandler handler = new XMLContentHandler();//設(shè)置defaultHandler
saxParser.parse(stream, handler);//進(jìn)行解析
stream.close();//關(guān)閉流
/*XMLReader xmlReader = saxFactory.newSAXParser().getXMLReader(); //獲取一個(gè)XMLReader
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(stream));
stream.close();*/
}catch(Exception e){
e.printStackTrace();
}
}
});
}
public class XMLContentHandler extends DefaultHandler {
private static final String TAG = "XMLContentHandler";
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
Log.i(TAG, "解析內(nèi)容:"+new String(ch,start,length));
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
Log.i(TAG, "文檔解析完畢。");
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
Log.i(TAG, localName+"解析完畢");
}
@Override
public void startDocument() throws SAXException {
Log.i(TAG, "開始解析... ...");
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
Log.i(TAG, "解析元素:"+localName);
if(localName.equals("high")){
Log.i(TAG, "解析元素:"+localName);
i++;
if(i==2){
highestTmp.setText(String.valueOf((Integer.parseInt(attributes.getValue(0))-32)*5/9));
}
}
}
}
上面的那段注釋:
[java] view plaincopy
/*XMLReader xmlReader =saxFactory.newSAXParser().getXMLReader(); //獲取一個(gè)XMLReader
xmlReader.setContentHandler(handler);
xmlReader.parse(newInputSource(stream));
stream.close();*/
是用XMLReader來做解析的另外一種方法。效果是一樣的。這里可以傳流,也可以傳一個(gè)字符串,如下所示:是傳字符串。
[java] view plaincopy
xmlReader.parse(new InputSource(new StringReader(xmlStr)));
PULL方式
除了可以使用 SAX和DOM解析XML文件,也可以使用Android內(nèi)置的Pull解析器解析XML文件。 Pull解析器的運(yùn)行方式與 SAX 解析器相似。它也是事件觸發(fā)的。Pull解析方式讓應(yīng)用程序完全控制文檔該怎么樣被解析。比如開始和結(jié)束元素事件,使用parser.next()可以進(jìn)入下一個(gè)元素并觸發(fā)相應(yīng)事件。通過Parser.getEventType()方法來取得事件的代碼值,解析是在開始時(shí)就完成了大部分處理。事件將作為數(shù)值代碼被發(fā)送,因此可以使用一個(gè)switch對(duì)感興趣的事件進(jìn)行處理。
Pull解析是一個(gè)遍歷文檔的過程,每次調(diào)用next(),nextTag(), nextToken()和nextText()都會(huì)向前推進(jìn)文檔,并使Parser停留在某些事件上面,但是不能倒退。然后把文檔設(shè)置給Parser。
Android中對(duì)Pull方法提供了支持的API,主要是
org.xmlpull.v1.XmlPullParser;
org.xmlpull.v1.XmlPullParserFactory;
二個(gè)類,其中主要使用的是XmlPullParser,XmlPullParserFactory是一個(gè)工廠,用于構(gòu)建XmlPullParser對(duì)象。
應(yīng)用程序通過調(diào)用XmlPullParser.next()等方法來產(chǎn)生Event,然后再處理Event。
我們?nèi)匀荒蒙鲜鎏鞖忸A(yù)報(bào)的XML文件的一部分來做例子。
例如:需要解析的XML文件是:
[java] view plaincopy
forecast_conditions
day_of_week data="周三"/
low data="22"/
high data="29"/
icon data="/ig/images/weather/chance_of_rain.gif"/
condition data="可能有雨"/
/forecast_conditions
這部分XML文件中day_of_week,low,high等是TAG,data是ATTRIBUTEA。當(dāng)然,如果有/夾在開始和結(jié)束符號(hào)之間的部分,則為TXET。
要想解析文檔先要構(gòu)建一個(gè)XmlPullParser對(duì)象。
[java] view plaincopy
final XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
final XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader("xmlStr");
這里的xmlStr就是上邊的XML文件。
此時(shí),文檔剛被初始化,所以它應(yīng)該位于文檔的開始,事件為START_DOCUMENT,可以通過XmlPullParser.getEventType()來獲取。然后調(diào)用next()會(huì)產(chǎn)生
START_TAG,這個(gè)事件告訴應(yīng)用程序一個(gè)標(biāo)簽已經(jīng)開始了,調(diào)用getName()會(huì)返回" day_of_week ";若有TEXT,則再next()會(huì)產(chǎn)生TEXT事件,調(diào)用getText()會(huì)返回TEXT,由于此處沒有,所以再next(),會(huì)產(chǎn)生END_TAG,這個(gè)告訴你一個(gè)標(biāo)簽已經(jīng)處理完了,再next()直到最后處理完TAG,會(huì)產(chǎn)生END_DOCUMENT,它告訴你整個(gè)文檔已經(jīng)處理完成了。除了next()外,nextToken()也可以使用,只不過它會(huì)返回更加詳細(xì)的事件,比如COMMENT, CDSECT, DOCDECL, ENTITY等等非常詳細(xì)的信息。如果程序得到比較底層的信息,可以用nextToken()來驅(qū)動(dòng)并處理詳細(xì)的事件。需要注意一點(diǎn)的是TEXT事件是有可能返回空白的White Spaces比如換行符或空格等。
nextTag()--會(huì)忽略White Spaces,如果可以確定下一個(gè)是START_TAG或END_TAG,就可以調(diào)用nextTag()直接跳過去。通常它有二個(gè)用處:當(dāng)START_TAG時(shí),如果能確定這個(gè)TAG含有子TAG,那么就可以調(diào)用nextTag()產(chǎn)生子標(biāo)簽的START_TAG事件;當(dāng)END_TAG時(shí),如果確定不是文檔結(jié)尾,就可以調(diào)用nextTag()產(chǎn)生下一個(gè)標(biāo)簽的START_TAG。在這二種情況下如果用next()會(huì)有TEXT事件,但返回的是換行符或空白符。
nextText()--只能在START_TAG時(shí)調(diào)用。當(dāng)下一個(gè)元素是TEXT時(shí),TEXT的內(nèi)容會(huì)返回;當(dāng)下一個(gè)元素是END_TAG時(shí),也就是說這個(gè)標(biāo)簽的內(nèi)容為空,那么空字串返回;這個(gè)方法返回后,Parser會(huì)停在END_TAG上。
小結(jié)一下,如果在一個(gè)XML文檔中我們只需要前面一部分?jǐn)?shù)據(jù),但是使用SAX方式或DOM方式會(huì)對(duì)整個(gè)文檔進(jìn)行解析,盡管XML文檔中后面的大部分?jǐn)?shù)據(jù)我們其實(shí)都不需要解析,因此這樣實(shí)際上就浪費(fèi)了處理資源。使用PULL方式正合適。
當(dāng)點(diǎn)擊三種方式的任何一個(gè)按鈕時(shí),均能夠得到相同的結(jié)果