本章將介紹代碼如何利用ZLTextPlainModel類來分別處理.xhtml文件中的文本信息與標籤信息。 html
本章涉及的核心類是ZLTextPlainModel類、ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction接口實現類 前端
.xhtml文件中包含着兩種信息:文本信息與標籤信息。咱們須要先正確解析出標籤信息表明的結構,才能正確得將文本信息顯示在屏幕上。 linux
舉個例子:(這個例子是三體1中的文本) c++
咱們須要讓程序知道這裏有四種標籤(h1標籤、h2標籤、b標籤、p標籤),每種標籤表明了不一樣的格式。程序必須正確顯示出不一樣標籤的格式,才能讓用戶看到正常的文本信息。 程序員
在正式開始介紹對.xhtml文件中的文本信息與標籤信息的處理流程以前,咱們有必要先來介紹下流程中涉及的三個核心類:ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction接口實現類 sql
ZLTextWritablePlainModel類是ZLTextPlainModel類的子類,這個類中有三個int數組與一個CachedCharStorage類。 數據庫
myStartEntryIndices屬性指向的int數組記錄了每一個段落具體在CachedCharStorage類內部的哪個char數組裏面; 數組
myStartEntryOffsets屬性指向的int數組記錄了每一個段落從CachedCharStorage類內部char數組的哪一個位置開始; 服務器
myParagraphLengths屬性指向的int數組記錄每一個段落在CachedCharStorage類內部char數組中佔據多少長度; 數據結構
最後,myStorage屬性指向的CachedCharStorage類內部的char數組則是實際存儲文本信息與標籤信息的地方
PS:FBReader程序中一組p標籤就表明一個段落(Paragraph)。
這個類中有兩個重要的屬性:myArray屬性、myBlockSize屬性
myArray屬性指向一個由char數組組成的ArrayList(char數組都設定爲軟引用WeakReference,保證了虛擬機會回收這些char數組,不會佔用過多的內存)。這些char數組裏面的元素就表明這.xhtml的文本信息與標籤信息。
myBlockSize屬性指向一個int。char數組的長度最長不會超過這個長度(65536),一旦超過這個長度,代碼就會新建一個char數組,同時就的數組會被持久化以便之後再用。
epub文件中有不少標籤,不一樣的標籤表明不一樣的不一樣的結構,因此FBReader也爲不一樣的標籤提供了不一樣的處理類。這些處理類都是XHTMLTagAction接口的實現類。
標籤通常都是成對出現的,XHTMLTagAction接口中的兩個方法分別就對應了
具體哪些類對應哪些標籤,是由XHTMLReader類中fillTagTable方法定義的。
介紹完三個核心類,咱們就能夠正式開始介紹對.xhtml文件中的文本信息與標籤信息的處理流程了。
咱們先以處理一個標籤對(包含起始標籤和結束標籤)的流程爲例。在利用for循環迭代標籤對轉換成的char數組的過程當中ZLXMLParser類的doIt方法會對如下的節點調用XHTMLReader類進行操做
起始標籤右邊的「<」:
記錄char數組的偏移量,調用ZLXMLReader接口characterDataHandlerFinal方法(XHTMLReader類並未實現該方法,故能夠忽略)
起始標籤右邊的「>」:
記錄char數組的偏移量,取出兩次偏移量當中的內容,獲得當前標籤的標籤名。
ZLXMLParser類的processStartTag方法 -> XHTMLReader類的startElementHandler方法 -> XHTMLTagParagraphWithControlAction類的doAtStart方法
結束標籤左邊的「<」:
記錄char數組的偏移量,取出兩次偏移量當中的內容,獲得標籤當中的文本信息
XHTMLReader類的characterDataHandler方法 -> BookReader類的addData方法
將標籤當中的文本信息存儲到BookReader類的myTextBuffer屬性
結束標籤右邊的「>」:
ZLXMLParser類的processEndTag方法 -> XHTMLReader類的endElementHandler方法 -> 標籤名對應XHTMLTagAction接口實現類的doAtEnd方法
將BookReader類中的myTextBuffer屬性
下面咱們再以《三體1》中的一段文本做爲例子來詳細介紹下這個流程:
記錄char數組的偏移量
記錄char數組的偏移量,取出兩次偏移量當中的內容,獲得當前標籤的標籤名。
ZLXMLParser類的processStartTag方法 -> XHTMLReader類的startElementHandler方法 -> XHTMLTagParagraphWithControlAction類的doAtStart方法
doAtStart方法
doAtStart方法會調用了兩個方法BookReader類的pushKind方法與beginParagraph方法
BookReader類的pushKind方法:
這個方法會在myKindStack屬性中添加FBTextKind.H1(31),而其實myKindStack屬性中已經有FBTextKind.REGULAR(0),這個屬性是在OEBBookReader類的readBook方法中設置的。
BookReader類的beginParagraph方法
這個方法調用了ZLTextWritablePlainModel類的createParagraph方法,而後用for循環迭代myKindStack屬性並調用ZLTextWritablePlainModel類的addControl方法
createParagraph方法更新了ZLTextWritablePlainModel類中的三個屬性,之後會依靠這三個屬性在CachedCharStorage類的char數組中快速定位某一個段落
addControl方法往CachedCharStorage類中的char數組加入了兩個能夠表明標籤的常量
PS:每次調用addControl方法都會加入ZLTextParagraph.Entry.CONTROL(3)這個常量,這個常量是一種標示。相似的標示還有常量ZLTextParagraph.Entry.TEXT(1),咱們會在下一章用到這兩種變量。具體這兩個標示是如何發揮做用的,請參考第十章中的內容。
記錄char數組的偏移量,取出兩次偏移量當中的內容,獲得標籤當中的文本信息
XHTMLReader類的characterDataHandler方法,將標籤當中的文本信息存儲到myTextBuffer屬性
ZLXMLParser類的processEndTag方法 -> XHTMLReader類的endElementHandler方法 -> XHTMLTagParagraphWithControlAction類的doAtEnd方法
doAtEnd方法會調用ZLTextWritablePlainModel類的addText方法,在CachedCharStorage類中的char數組中加入三種信息:
1、常量ZLTextParagraph.Entry.TEXT(1),這是一種標示,相似常量ZLTextParagraph.Entry.CONTROL(3)
2、標籤間文本信息的長度
3、標籤間的實際文本信息
H2標籤、P標籤與H1標籤基本上是同樣,惟一的區別在於在起始標籤右邊的「>」觸發的addControl方法中加入不一樣的常量,而這個變量實際上是在FBTextKind接口中定義的。
H2標籤會加入FBTextKind.REGULAR(0)以及FBTextKind.H2(32),P標籤則只會加入FBTextKind.REGULAR(0)。每次加入這些常量的時候都會同時加入做爲標示的常量ZLTextParagraph.Entry.TEXT(1)。
B標籤與其餘三個標籤不一樣,這個標籤會觸發兩次addControl方法,只是兩次的參數不一樣。
這裏須要補充下CachedCharStorage類中新增char數組的流程,咱們在介紹CachedCharStorage類的時候,曾經講過:「BookModel類中的myBlockSize屬性指向一個int。char數組的長度最長不會超過這個int(65536),一旦超過這個長度,代碼就會新建一個char數組,同時就的數組會被持久化以便之後再用。」
新增char數組的工做由CachedCharStorage的createNewBlock方法完成
將舊的char數組持久化的工做是在CachedCharStorage類的freezeLastBlock方法完成的。
在sd卡上的Books/.FBReader這個文件夾裏面,咱們能夠找到這些持久化了的文件。
這個位置是在BookModel的構建函數中用過Paths類得到的。
其實咱們能夠嘗試把持久化char數組的方法改成utf8的編碼,而後在把獲得的文件後綴名改爲.txt
打開這個txt文件,咱們就能夠看到下面這樣的數據
對比原先的xml文件,那些奇怪的符號就表明了標籤信息
好,至此爲止,咱們將.xhtml文件中的文本信息與標籤信息存儲到了ZLTextPlainModel中。而要想讓程序最終用中正確的格式顯示文本信息,還須要配合以後第八章(定位指定段落)以及第九章(顯示.html文件)的內容才能讓用戶看到格式正確的文本。
最後,插幾句題外話,寫一些本身的思考:
FBReader使用char數組的形式來存儲xml文件內容與結構信息,而後依靠記錄每一個段落在char數組中具體位置的int數組快速獲取指定段落在char數組中的部分。選擇數組是ok的,在數組中定位到某一個部分,速度仍是比較快的,可是同時,也由於使用了數組這個數據結構,程序也是相對來講比較佔用內存的。通常來講,電子書都是從一整段數據中按順序取出一部分數據顯示在屏幕上,這種業務需求其實用樹的數據結構也是很是合適的。而說到樹的數據結構,其實Android手機中正好有一個現成的sqlite數據庫能夠提供這種樹的數據結構。咱們能夠試想一下,若是改用sqlite數據庫來存儲和檢索xml文件的內容與結構信息的話,相比數組會有怎樣的好處。我想最起碼會有三個好處:第1、節省手機的內存,sqlite數據庫作好索引以後,無需像數組那樣把數據所有裝進內存後才能檢索。這樣一來,程序就節省了內存;2、方便跨平臺的開發,sqlite支持linux、IOS以及HTML5,若是使用sqlite來做爲存儲和檢索xml文件的方式,那麼Android程序員、IOS程序員以及pc前端程序員只須要根據約定的sql語句就能完成開發,而沒必要獨立開發三種語言,或者用c或c++另外再開發一個底層庫;3、方便與服務器端對接,當須要對客戶端用戶的閱讀記錄進行收集與分析的時候,若是客戶端與服務端使用同一種或相近的sql結構的話,那麼對接的難度就會下降不少。