第三章、获取书籍信息

第三章、获取书籍信息

在第一章与第二章中我们将精力集中在了主线程上,介绍了主线程是如何控制一个进度条的显示和消失,并通过解析资源文件在进度条上显示合适的文字。

从这一章节开始,我们会将精力转到子线程上。本章将介绍后台运行的子线程获取包括文件路径在内等书籍信息的流程。

本章涉及的核心类包括FBReaderApp类、SQLiteBooksDatabase类、Book类、BooksDatabase类、FileInfoSet类

子线程运行的代码其实就是FBReaderApp类initWindow方法中定义的Runnable类中的内容。

正常情况下,因为是第一次进入程序,book变量会是null,代码就会调用getHelpFile方法。然后把帮助文件显示出来。帮助文件是一个格式为fb2的文件,并不是我们常用的epub格式。

为了介绍epub的处理流程,我们就不从默认的处理fb2格式的帮助文件流程入手了,而是假设我们上一次有阅读过一个epub文件,这样Library类的getRecentBook方法就会找到上一次阅读过的epub文件。

我这里选取的用来测试的epub文件是《三体I 地球往事》,权当是向大刘致敬。(这个epub只用作测试,请大家支持正版,多看上有三体合集的购买地址,我自己买的就是这个合集)

回到代码上来,因为参数myArg0都是null,所以book变量也就是null。接下来我们就会进入到Library类的getRecentBook方法。

LibrarygetRecentBook方法:

这个方法会调用SQLiteBooksDatabase类的loadRecentBookId方法

    SQLiteBooksDatabaseloadRecentBookId方法

loadRecentBookId方法其实就是运行了一句sql语句,这句sql语句会从book_index表中取出book_id的值。

看到这句sql语句,我们很自然得就会想去寻找初始化整个Sqlite数据库的代码所在的位置。对sql数据初始化的代码就是SQLiteBooksDatabase类的构造函数


        SQLiteBooksDatabase类构造函数

SQLiteBooksDatabase
类的构造函数是在
FBReader
类中的
createApplication
方法中被调用的。

createApplication方法则是被FBReader的父类ZLAndroidActivity中的onCreate方法调用。

还记得我们在第一章一开始就有说过:“进入到FBReader类,此时首触发的是FBReader类的父类AndroidActivity中的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方法。

        SQLiteBooksDatabasemigrate方法

进入到migrate方法里面,一眼就能看到一个我们第一章中曾经遇到果断的UIUtil类wait方法。这个方法的作用就是在屏幕上显示一个显示等待的进度条,然后从从资源文件中找出特定的字符串,填充到进度条里面去。具体的代码大家可以去查看第一章的内容。

createTables方法负责创建表,创建索引之类的事情则是由一系列的updateTablesX方法来完成了。

回到FBReaderApp类getRecentBook方法:

    BookgetById方法

这个方法会从RecentBooks这张表里面返回book_id,然后会调用Book类的getById方法利用这个book_id去获得书籍的详细信息。
     

Book类的getById方法会调用到SQLiteBooksDatabase的loadBook方法

loadBook方法从Books这张表里面取出书籍的相关信息

接着,loadBook方法调用了BooksDatabase类createBook方法

        BooksDatabasecreateBook方法        

BooksDatabase类createBook方法调用了两个方法:FileInfoSet类的构造方法与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两个属性。

BooksDatabasecreateBook方法:

BooksDatabase
类的
createBook
方法首先调用了
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文件处理流程了。

时间: 2024-10-02 09:38:47

第三章、获取书籍信息的相关文章

C# 网络编程之通过豆瓣API获取书籍信息(一)

这篇文章主要是讲述如何通过豆瓣API获取书籍的信息,起初看到这个内容我最初的想法是在"C# 网络编程之网页简单下载实现"中通过HttpWebResponse类下载源码,再通过正则表达式分析获取结点标签得到信息.但后来发现可以通过豆瓣API提供的编程接口实现. 该文章仅是基础性C#网络编程文章,尝试测试了下豆瓣API,并没什么高深的内容.但希望对大家有所帮助,仅供学习. (警告:文章仅供参考,提供一种想法,否则访问多次-10次被403 forbidden莫怪.建议认证使用豆瓣API) 一

Cedar老师的java中的反射学习笔记(三)--获取方法信息

基本的数据类型,void关键字都存在类类型,通过getClass();获取到 (1)基本数据类型--类类型int.class -->int 的类类型String.class-->String类的 类类型double.class-->double的类类型Double.class-->Double类的 类类型void.class -->void 的类类型 Class类的基本api: (2)一个成员方法就是一个method对象 getMethods() -->public的方

ASP.NET Core 打造一个简单的图书馆管理系统(五)初始化书籍信息

前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows <Pro ASP.NET MVC 5>.<锋利的 jQuery> 此系列皆使用 VS2017+C# 作为开发环境.如果有什么问题或者意见欢迎在留言区进行留言.

2017上半年软考 第三章 重要知识点

第三章 讲了信息系统集成所需要的技术: 重点是:信息系统生命周期:立项[形成需求规格说明书].开发.运维.消亡: 信息系统建设包括:设备采购.系统集成.软件开发.运维服务: 软件开发常用方法:结构化方法[整个系统分若干阶段依次进行.每个阶段都有详细的文档编制要求:注重全局和整体性: 缺点开发周期长,文档设计繁琐,设计说明繁琐,工作效率低,要在开发之初认识系统需求].原型法[快速开发一个原型.反复修改来实现用户需求: 分抛弃型原型.进化型原型].面向对象法[关键:能否建立全面.合理.同意,反映需求

(第三章,第四章)http报文内的http信息,返回结果的http状态码

第三章 http报文内的http信息 用于http协议交互的信息被称为http报文,包括请求报文和响应报文. 1.编码提升传输速率,在传输时编码能有效的处理大量的访问请求.但是编码的操作是计算机完成的,会消耗更多的cpu资源. 2.压缩传输的内容编码: 内容编码后的实体由客户端接受并负责解码. 3.分割发送的分块传输编码 在传输大量数据时,通过数据分割成多块,能够让浏览器逐步显示页面. 4.获取部分内容的范围请求: 可以处理大文件突然下载中断的问题. 5.内容协商返回最合适的内容 比如根据浏览器

第三章 HTTP报文中的HTTP信息

第三章 HTTP报文中的HTTP信息 HTTP通信过程:客户端—>服务端,服务端—>客户端. 1.HTTP报文 使用HTTP协议交互的信息被称为HTTP报文,包括请求报文和响应报文. [请求报文和响应报文结构]报文首部(请求行/相应行+首部字段<请求.响应.通用.实体首部字段>)+报文主体  2.报文编码 HTTP在传输数据是可以通过编码技术来有效处理大量的访问请求,但会消耗更多的CPU等资源. [报文和实体]报文是箱子,是HTTP通信中的基本单位,由8位组字节流组成,通过HTTP

微信小程序把玩(三十八)获取设备信息 API

原文:微信小程序把玩(三十八)获取设备信息 API 获取设备信息这里分为四种, 主要属性: 网络信息wx.getNetWorkType, 系统信息wx.getSystemInfo, 重力感应数据wx.onAccelerometerChange, 罗盘数据wx.onCompassChange wxml <button type="primary" bindtap="getNetWorkType">获取网络类型</button> <butt

Java 反射理解(三)-- Java获取方法信息

Java 反射理解(三)-- Java获取方法信息 基本的数据类型.void关键字,都存在类类型. 举例如下: public class ClassDemo2 { public static void main(String[] args) { Class c1 = int.class;//int 的类类型 Class c2 = String.class;//String类的类类型,可以理解为String类字节码 Class c3 = double.class; Class c4 = Doubl

《JavaScript设计模式》笔记之第三章:封装和信息隐藏

第三章 创建对象的基本模式 方法一:门户大开型 var Book = function(isbn, title, author) { if(isbn == undefined ) throw new Error ('Book constructor requires an isbn.'); this.isbn = isbn; this.title = title || 'No title specified'; this.author = author || 'No author specifi