在第一章與第二章中咱們將精力集中在了主線程上,介紹了主線程是如何控制一個進度條的顯示和消失,並經過解析資源文件在進度條上顯示合適的文字。 sql
從這一章節開始,咱們會將精力轉到子線程上。本章將介紹後臺運行的子線程獲取包括文件路徑在內等書籍信息的流程。 shell
本章涉及的核心類包括FBReaderApp類、SQLiteBooksDatabase類、Book類、BooksDatabase類、FileInfoSet類 數據庫
子線程運行的代碼其實就是FBReaderApp類initWindow方法中定義的Runnable類中的內容。 windows
正常狀況下,由於是第一次進入程序,book變量會是null,代碼就會調用getHelpFile方法。而後把幫助文件顯示出來。幫助文件是一個格式爲fb2的文件,並非咱們經常使用的epub格式。 函數
爲了介紹epub的處理流程,咱們就不從默認的處理fb2格式的幫助文件流程入手了,而是假設咱們上一次有閱讀過一個epub文件,這樣Library類的getRecentBook方法就會找到上一次閱讀過的epub文件。 工具
我這裏選取的用來測試的epub文件是《三體I 地球往事》,權當是向大劉致敬。(這個epub只用做測試,請你們支持正版,多看上有三體合集的購買地址,我本身買的就是這個合集) 測試
回到代碼上來,由於參數myArg0都是null,因此book變量也就是null。接下來咱們就會進入到Library類的getRecentBook方法。 spa
這個方法會調用SQLiteBooksDatabase類的loadRecentBookId方法 .net
loadRecentBookId方法其實就是運行了一句sql語句,這句sql語句會從book_index表中取出book_id的值。 命令行
看到這句sql語句,咱們很天然得就會想去尋找初始化整個Sqlite數據庫的代碼所在的位置。對sql數據初始化的代碼就是SQLiteBooksDatabase類的構造函數
createApplication方法則是被FBReader的父類ZLAndroidActivity中的onCreate方法調用。
還記得咱們在第一章一開始就有說過:「進入到FBReader類,此時首先觸發的是FBReader類的父類ZLAndroidActivity中的onCreate方法。」因此,在程序剛剛開始運行的時候sqlite的數據庫就被建立了。
好了,如今咱們再來詳細介紹下SQLiteBooksDatabase類的構造函數。
openOrCreateDatabase方法就是新建或打開一個books.db的數據庫。看到這段代碼,咱們能夠知道每一個運行過FBReader程序的手機上都會有一個books.db數據庫。那麼這個數據庫到底是在哪裏呢?數據具體在什麼地方實際上是否是有openOrCreateDatabase方法的第一個參數決定的。若是這個參數是一個絕對路徑,那麼數據庫就會創建到這個路徑裏面去;若是這個參數是一個相對路徑,那麼數據庫就會創建到程序默認的文件夾裏面去。代碼中,openOrCreateDatabase方法第一個參數就是使用了相對路徑,因此數據庫創建程序默認的文件夾裏面的。通常來講,數據庫比較小,那麼方法在程序的默認的文件夾裏面是沒有問題的。但若是數據庫比較大的話,那麼就要考慮是否須要放置到sd卡里面去了。
接下來咱們去尋找下FBReader的默認文件夾。
通常來講,安裝到手機上的程序都會安裝到/data/datad的目錄下。要找到這個目錄,你的手機必須是已經root過的。如今,咱們不妨來找一下這個數據庫。
在cmd裏面經過adb shell命令(安裝完sdk,要記得把platform-tools的路徑加到環境變量裏面去,不然adb命令就不能運行了)和su命令(沒有root的話,就沒有su命令),而後經過cd /data/data進入文件夾
全部的安裝到手機上的程序通常都會安裝到/data/datad的目錄下,可是這個目錄下的文件夾很是得多,FBReader具體是哪一個文件夾呢?
其實FBReader文件夾的名字是由AndroidManifest.xml裏面的package屬性決定的。這個屬性的值是什麼,FBReader所在的文件夾名字是什麼。
咱們如今就能夠進入這個文件夾,數據庫就是在這個文件夾下的databases文件夾下了
咱們能夠sqlite3 books.db的命令直接進入數據庫查看。
進入數據庫以後,使用.tables的命令查看下數據庫裏面到底有哪些表
可是,使用windows的命令行查看books.db會有字符集的問題。sqlite默認的字符集是UTF-8,咱們能夠用pragma encoding命令查看默認的字符集。
可是windows的命令行倒是GBK的字符集,這樣一來數據庫裏面的中文都會是亂碼。
爲了不字符集的問題,建議你們可使用可視化工具來查看sqlite數據庫。我使用的是navicat for sqlite,下載地址是http://download.csdn.net/download/zar19901007/6417605
繼續回來講數據庫,從咱們能夠從navicat for sqlite看到表還很多,初始狀態下這些表確定都是沒有的,都須要代碼建立出來的。而完成建立工做的就是SQLiteBooksDatabase類的migrate方法。
進入到migrate方法裏面,一眼就能看到一個咱們第一章中曾經遇到果斷的UIUtil類wait方法。這個方法的做用就是在屏幕上顯示一個顯示等待的進度條,而後從從資源文件中找出特定的字符串,填充到進度條裏面去。具體的代碼你們能夠去查看第一章的內容。
createTables方法負責建立表,建立索引之類的事情則是由一系列的updateTablesX方法來完成了。
回到FBReaderApp類getRecentBook方法:
這個方法會從RecentBooks這張表裏面返回book_id,而後會調用Book類的getById方法利用這個book_id去得到書籍的詳細信息。
Book類的getById方法會調用到SQLiteBooksDatabase的loadBook方法
loadBook方法從Books這張表裏面取出書籍的相關信息
接着,loadBook方法調用了BooksDatabase類createBook方法
BooksDatabase類createBook方法首先調用了FileInfoSet類的構造方法,而後又遞歸得調用了其自己,咱們一個一個來看。
這個構造函數裏面有兩個方法,分別是SQLiteBooksDatabase類的loadFileInfos方法和FileInfoSet類load方法。
SQLiteBooksDatabase類的loadFileInfos方法會進入一個while循環,這個循環不斷得得到file_id對應的parent_id,一直到parent_id爲null。
代碼會針對file_id爲57和64的項目代碼會調用BooksDatabase類的createFileInfo方法,新建一個FileInfo類(此時FileInfo類的構造函數中的parent參數爲null)。
建立出來的FileInfo類被加入到了infos變量所指向的ArrayList中
在while循環以後,代碼再利用for循環,從infos變量裏面依次把元素取出來,從新調用一遍createFileInfo方法,這一次新建FileInfo類是parent參數就不是null了。新建的FileInfo類最後又被放回infos變量所指向的ArrayList中
代碼跳出SQLiteBooksDatabase類的loadFileInfos方法後就會進入FileInfoSet類load方法,這個方法更新了myInfosByPair、myInfosById兩個屬性。
咱們以前說過BooksDatabase 類的 createBook 方法會不斷遞歸得調用自身,直到getFile 方法返回null時,代碼纔會跳出這個遞歸流程。
首先調用了 FileInfoSet 類的 getFile 方法,這個方法就是從剛剛更新過的 myInfosById 屬性裏面根據 fileId ,取出對應的 FileInfo 類。
再根據FileInfo類的信息會做爲參數被FileInfoSet類的另外一個getFile方法。這個方法利用了遞歸,只要FileInfo的parent屬性不是null,遞歸就會繼續。
在這個遞歸中,會兩次調用ZLFile類的createFile方法。
第一次進入createFile方法的時候,第一個參數是null,會創建一個ZLPhysicalFile類,表明/storage/emulated/0/Ebook這個文件夾
第二次進入createFile方法的時候,參數是剛剛創建的ZLPhysicalFile類,此時會創建一個新的ZLPhysicalFile類,這個新的ZLPhysicalFile類將表明epub文件
注意,新建的ZLPhysicalFile類構造函數的參數會是epub文件的完整路徑:「/storage/emulated/0/Ebook/三體1《地球往事》.epub」。構造函數會利用這個完整路徑建立一個ZLPhysicalFile類。正因這裏設置了File類,因此才能在getInputStream方法中返回FileInputStream類。
另外,ZLPhysicalFile類的構造函數調用了ZLFile類的init方法。這個方法設定了myExtension屬性,這個屬性在第六章「epub文件處理 -- 解析 container文件與.opf文件」中會被用到。
當FileInfoSet類的getFile方法跳出時,返回的是第二次創建的ZLPhysicalFile類。這個ZLPhysicalFile類將做爲參數被BooksDatabase類的createBook方法調用。
最終,createBook類返回了一個新建立的Book類。
到這裏爲止,Library類的getRecentBook方法的流程就走完了,程序獲取了book變量指向的Book類。代碼以這個類爲參數,調用了FBReaderApp類的openBookInternal方法。從這個方法開始,咱們就開始對epub文件處理流程了。