摘要

基于插件式架构的安卓增量数据源

小说阅读器应用程序的设计

摘要

互联网上有众多小说阅读和资讯阅读的网站,它们大多独立制作自己网站的应用, 这导致各种阅读类应用的用户体验不一,而且更多数的网站并没有提供手机应用,每次 获取资讯都需要打开网站,所以设计一个通用的阅读器,并且将各种阅读资源网站以插 件的形式接入,十分有利于用户的便捷体验。

文章中介绍了一个完整的Android MVC应用的结构和开发的流程,对于开发中的各 个环节都有所涉及,设计方面采用了Google公司于2014年发布的Material Design设计 标准,用到了Android 5.0以上版本的新特性,是一个完全符合现代移动互联网应用设 计标准的毕业设计。

关键词:安卓,小说阅读,阅读器,应用开发

I

毕业设计

DESIGN OF ANDROID NOVEL READER APPLICATION

BASED ON PLUG-IN FRAMEWORK

ABSTRACT

There are various novel reading websites and news material reading websites in nowadays Internet. Most of them have there own mobile app, and this causes the differences of user experience. However, there are still lots of websites does not have their own app. As a result, people who want to fetch information from their websites must open the website through the web browser. It is handy to design and make such a universal app, to accept news material and novel material websites as plug-ins.

In this article, I introduce the whole procedure of the development of an Android application project, which based on MVC design pattern. Addtionally, this article contains varieties of technologies that are used in this project. In terms of UI design, I obey the rules called Material Design, which is published by Google Inc. in 2014. And the new features of Android 5.0+ are included as well. In conclusion, this graduation design is fully up to date.

application development

II

目录

目录

1

毕业设计

2

1引言

1引言

随着智能手机硬件配置的提高、移动通信资费的降低,移动设备已经完全融入了人 们的生活中,通过手机连接互联网我们可以完成几乎涵盖生活方方面面的事情。衣食住 行,全部可以通过指尖轻点完成,这些都是与生活息息相关非常实用的功能,总的来说 对于人们来说移动设备的使用利大于弊。于是在实用功能的开发之外,智能设备已经开 始向娱乐功能发展了,包括手机游戏、音乐视频的播放、小说阅读等,这些娱乐功能无 一不给人们的智能生活带来快乐。

本毕业设计旨在对手机阅读应用进行进一步革新,让人们的娱乐体验升级。

1.1手机阅读器现状分析

当今时代,是信息大爆炸的时代,人们每天都能接收到数不清的各类信息,其形式 不仅包含传统的文字和图片,还有视频、音频等多媒体。媒体的来源也丰富多样,由传 统的可信官方媒体,到现如今的网络自由撰稿人;由以往的官方组织的资讯站,到个人 站长建立的阅读站。阅读资料源(提供阅读资料的网站)也较以往增长了太多。

与本设计相关的情况是,目前互联网上有众多小说阅读和资讯阅读网站,一部分做 的比较大网站的就会独立制作自己的应用供用户下载使用,因此市面上各种阅读类应用 的用户体验参差不齐;另一方面,绝大多数的网站并没有提供应用,甚至连适配手机的 网页(WAP或响应式页面)都没有提供。

所以设计一个通用的阅读器,并且将各种阅读资源网站以插件的形式接入,十分有 利于用户的便捷娱乐体验。

1.2插件架构的手机阅读器的可行性分析

手机应用的开发主要依赖于手机操作系统、开发环境、技术基础以及开发资料等, 以下从这几个方面分析本设计的可行性。

1.2.1 Android 手机操作系统

Android手机操作系统由于其开源的特性,众多手机厂商都使用该系统作为出厂操 作系统,据统计截至2015年9月全球活跃用户量已经超过了14亿。另一方面,Android系 统发展十分迅速,短短的5年时间已经从一个操作系统的雏形变成了一个功能强大且完 备的操作系统,并且从2014年的Android 4.2开始,Android操作系统已经逐步支持4G网 络通信,而截至2016年2月国内4G用户量已达5亿,可以说为Android操作系统开发应用 有十分庞大的潜在用户优势。

1

毕业设计

1.2.2 Android Studio 开发环境

Google公司在2014年公布了一款基于Jet Brains公司的旗舰产品——IntelliJ IDEA的全新Android IDE——Android Studio,并且使用Gradle构建整个Android应用, 是非常新且非常便捷的构建工具,截至2016年5月,该工具的最新版本为2.1.0。加之以 前主推的Eclipse的ADT套件已经不再维护,全球的开发者都在使用这个开发工具进行 Android应用的开发,可见这套IDE已经非常成熟了。

1.2.3 Java 技术基础

据Google官方统计,截至2016年5月2日,Android 4.0.3以上版本的用户占Android 用户总数的97.7%,而Android 4.0.3全面支持Java7,这一Java版本是目前使用最广泛的 Java版本,且较上一版本Java6没有太多改进。开发者只需要基本的Java SE语法即可驾 驭Android开发全部环节。

1.2.4 Android 应用开发资料

Android已经开源6年多了,其4.0版本距离现在也有近3年的历史,并且随着GitHub、 SourceForge、Bitbucket等开源社区热门化的浪潮,全球顶尖开发者高质量的代码都唾 手可得,这些都是学习Android应用开发的捷径。

开发问答社区,例如:StackOverflow、SegmentFault,都有着良好地解决问题的 氛围,开发时遇到的报错、BUG等问题几乎全部可以在这些社区中找到答案。截至2016 年5月18日,StackOverflow的Android板块的问题量已经达到了89万条。每次开发遇到 不了解的问题,在该站上搜索总能找到相同或者类似的问题,如果很巧合没有别人遇到 这个问题,那么可以自己详细提问,一般24小时内都会有人给出思路。

除了以上这些第三方的资料以外,官方指导资料也很丰富,在Android的官方网站 上有无数的新特性的开发指南,所以Android的开发资料十分丰富。

1.3手机阅读发展趋势

手机小说阅读的发展大致分为如下几个阶段:

1.WAP网页阅读,每个页面结构非常简单,数据量也非常小。

2.TXT纯文本阅读,脱离的网络的束缚,一个设备可以存储更多的小说,节约流量。

3.自动重排网页阅读,网页虽然体积较大,消耗流量较多,但是色彩和样式较为 丰富,很多也带有图片,主要依赖于手机浏览器应用的自动网页重新排版功能。

IDE = Integrated Development Environment (集成开发环境),下同。

ADT = Android Development Tools,是 Google 公司为 Eclipse 开发的 Android 开发套件,下同。

数据计算方法:http://stackoverflow.com/questions/tagged/android 页面底部显示总页数为 55870,乘以每页

16 条,总共是约 893920 条问题记录。

2

1引言

4.针对手机优化的网页阅读,诸如响应式设计的网页以及针对手机开发的移动页 面,都可以让手机阅读体验更加上一个层次。

5.手机客户端阅读,能够更加便捷地引入支付功能,便于提供受版权保护的阅读 内容,而且客户端的推送功能以及用户的社交功能全部集成在一起。

以上发展流程主要是大公司的发展轨迹,但是目前互联网内容大爆发,大多做的让 网友满意的都是个人阅读站点,这些站点虽然资源非常丰富,但是因为个人能力有限, 阅读体验并不好。加之现在主推的大数据,大家都很推崇数据融合等聚合类的功能,例 如推酷、51CTO、ITeye等网站的运营方式都是将互联网中技术型博客的内容进行整 合,由搜索引擎带来的访问量极为庞大。

由此不难预测,未来的小说阅读也是聚合型的平台主导。

1.4手机阅读资源的版权问题

本应用对于网络数据源处理的设计性质和手机浏览器类似,通过对用户有权限访问 的网站页面进行请求,获取服务器返回的数据,然后加以过滤提纯,最后展现给用户。 数据处理的原理类似于手机上的网络爬虫,也即和搜索引擎类似。

但不同的是,本应用的不提供任何存储服务。所以如果涉及到版权问题时,和手机 浏览器的性质是一样的,由盗版内容提供者和使用者负全责,本应用也是完全合法的。

推酷:http://tuicool.com/

51CTO:http://www.51cto.com/

ITeye:http://iteye.com/

3

毕业设计

2插件式架构安卓阅读器的关键技术

一个系统设计的基础就是掌握所用工具的特性,本应用设计所用工具的关键特性决 定了开发时要做的框架与细节处理。

2.1 Android 操作系统

Android操作系统目前主要由Google公司维护,是一个基于Linux内核、专为触屏移 动设备设计的操作系统。其与用户的交互主要依赖于用户直接的信息输入——触摸手 势,例如:滑动手势、点击手势、拖动手势等,包括用于输入文字的虚拟键盘也是基于 这些操作。

现在Android操作系统在智能电视、智能穿戴设备,甚至智能交通上的应用越来越 广泛,而且针对每一种设备Android系统都提供了不同的交互界面,充分发挥各种设备 的特性。截至2016年2月,公开发布的Android应用数量已达2000万,大部分应用都可以 在各种移动设备中完美运行,这全部归功于Android核心的各种特性。

2.1.1 Dalvik 虚拟机

Dalvik虚拟机也写作DalvikVM,是Google公司专为Android开发的一款JVM,其负 责运行由JVM语言编写的Android应用,其相对于Oracle公司的JVM来说做了很多优化。 比如DalvikVM基于寄存器,而JVM基于堆栈;DalvikVM执行的是特有的DEX文件格式,而 传统的JVM运行的是*.class类文件。

较传统JVM而言,主要有如下优势:

1.在编译时提前优化代码,不必浪费运行时动态优化代码的性能。

2.虚拟机本体代码很小,占用空间也小,可以高效运行多个虚拟机实例。

3.原来每个*.class类文件中有一个常量池,现在由DEX文件中唯一常量池来管理。 这样做虽然对于系统来说优化了很多,但是对于开发者的限制就增加了,例如传统

JAR包在JVM可以很轻松地动态加载,换成DalvikVM则需要开发者首先应该由JAR包生成 DEX包,并且在运行时DalvikVM需要进行动态优化后才能运行。

2.1.2 Java 本机接口

Java本机接口也即JNI,是JVM提供给其运行环境的一种原生调用方式。

它的设计目的是为了允许JVM的代码与其他语言进行交互,但这样做通常会导致丧

JVM = Java Virtual Machine (Java 虚拟机),下同。

JVM 语言包括:Java、Clojure、Scala、Kotlin 等,它们都可以用来编写 Android 系统的应用程序。

JNI = Java Native Interface (Java 本机接口,也叫:Java 本地调用),下同。

4

2插件式架构安卓阅读器的关键技术

失平台可移植性,所以一般只在特定的需求下进行,例如:用旧的其他语言的库、大量 数学计算需要性能调优时。

虽然Android使用的不是传统的JVM,而是DalvikVM,但其同样提供了JNI。Google 官方推荐的JNI使用场景是大量的计算,例如手机游戏这样的大量图形计算,在阅读器 中翻页动画的实现也是一种应用场景——JNI的OpenGL调用。

对于JNI的兼容性,Google官方也提供了一种解决方案:为各个处理器编译不同的 二进制文件。目前Android运行的处理器平台总共只有如下这些:armeabi、armeabi-v7a、 arm64-v8a、mips、mips64、x86、x86_64,它们均可以由Google官方推出的NDK编译出 来,唯一的弊端就是需要牺牲软件安装包的体积。

2.1.3 国际化 i18n

“i18n”即国际化,表示一个应用运行时可以选择或自动选择某个国家的语言作 为界面的显示语言。

Android系统中实现i18n非常容易,只需要在项目的src目录中建立对应国家的 values目录,并在该目录下建立strings.xml文件把字符串的各国语言翻译文本填入对 应条目中即可,在Android运行时调用R.string.string_id即可访问当前系统语言对应 的翻译文本。另外Android Studio提供的翻译编辑器(如图2.1.3-1)也简化了编辑。

图2.1.3-1 翻译编辑器

除了访问当前系统语言对应的文本之外,还可以通过访问API获取各个语言的字符 串,所以在应用中内置语言切换功能也是十分容易的。

NDK = Native Development Kit (原生开发工具),下同。

i18n = internationalization (国际化,因英文单词去掉前导 I 和后导 N 中间一共 18 个字符而得名),下同。 API = Application Programming Interface (应用程序开发接口),下同。

5

毕业设计

2.1.4系统版本与兼容

截至2016年5月,Android在市面上的几个主要版本是4.0.3、4.0.4、4.1.x、4.2.x、

4.3、4.4.x、5.0.、5.1、6.0。Android SDK是每一个API版本都会推出一套,所以要 让尽可能多的用户能够运行该应用,选择一个合适的最低支持版本号非常重要,Google 官方也给出了推荐:从4.0.3开始支持,实际上Google的全部应用也都是这样做的。

但是由于不同版本之间会有特性的差异,比如Android 4.4+支持了状态栏和导航栏 的透明和变色效果,Android 5.0+的版本上则系统级地支持了全新的Material Ripple 效果,以及ART运行模式等。这些不同的特性,尤其是Android 5.0的新特性都是用户 希望能在应用中体验到的,开发者则需要在代码中检查当前系统版本,启用不同的视觉 效果,因为高版本的API调用运行在低版本的系统中会直接停止运行。

除了版本的差异之外,运行Android的设备屏幕差异也很大,屏幕的规格和DPI各 不相同,应用界面的布局在各种设备上运行的效果都是不一样的,开发者可能需要针对 不同的设备建立不同的布局,达到适配各种屏幕的目的。

这些需要考虑的地方做起来比较麻烦,但是实现的时候已经有无数的开源项目可以 直接使用,Google官方也有兼容库,专门负责高版本的特性向低版本兼容。

2.1.5资源格式标准

Android项目的src文件夹主要存放应用程序用到的各种资源,其目录结构在近几个 版本也发生了一些改动,而之前版本的结构则显得相对混乱。以下的资源目录结构是 Google当前(2016年)推荐的:

1. animator/

存放少量属性属性动画的XML定义文件。

2. anim/

存放属性动画较复杂的XML定义文件,一般每个动画包括数十个属性的改变。

3. color/

存放颜色状态列表的XML定义文件。

4. drawable/

存放图片文件(只能是*.png、*.9.png、*.jpg、*.gif格式的),或表示图片、9 切片、状态列表、形状、动画图片等的XML定义文件。

5.mipmap/

SDK = Software Development Kit (软件开发工具),下同。

ART = Android RunTime (安卓运行时),下同。

DPI = Dots Per Inch (每英寸像素点数),下同。

6

2插件式架构安卓阅读器的关键技术

存放针对不同屏幕像素密度的图片文件。

6. layout/

存放用户界面布局的XML定义文件。

7. menu/

存放应用中使用到的各种菜单的XML定义文件,例如:选项菜单、上下文菜单等。

8. raw/

存放可以用InputStream访问的无任何格式要求的文件。

9. values/

存放储存单值的XML定义文件,可以是字符串、数字、颜色等数值。

10. xml/

存放任何XML文件,可以在应用程序运行时调用Resources.getXML()方法获取。 整个资源结构改动较大的部分就是图片部分了,以前都是把各种图片资源统统存储 在drawable/文件夹中,现在drawable/文件夹存储的大多是矢量的SVG图标,各种不同

大小的切图文件全部存放在mipmap/文件夹中,使得资源的存放比较规整。

2.2插件架构

本毕业设计的创新点是一个插件框架,用于动态给程序增加新的功能,或者更新现 有功能。由于程序的原理是抓取现有网站的内容并提取提纯页面内容,所以网站的任何 页面结构改动都有可能导致插件解析小说内容的失败,因此插件需要能及时更新,保证 和网站的结构更新同步。

目前插件的实现有如下几种思路:

1.函数包,通过传递参数和接收返回参数实现插件的处理。

2.继承自某个内建抽象类,实现所有需要的处理调用。

3.实现某些内建的接口即可,调用者只需按照接口调用,便于组装。

2.2.1 DEX 插件

Android中DalvikVM动态加载DEX类似于JVM中动态加载JAR包,都是虚拟机原生支持 的动态加载方式,其执行效率也比其他脚本语言要高很多。

最强大的一个特点是它的面向对象特性,其可以有选择地加载DEX中的类,如果DEX 中的类是从程序中的类继承的,则可以直接类型转换为相应的类,无需像其他解释型脚

文件夹的命名目前只能是 mipmap-ldpi/、mipmap-mdpi/、mipmap-hdpi/、mipmap-xhdpi/、mipmap-xxhdpi/、 mipmap-xxxhpi/、mipmap-nodpi/、mipmap-tvdpi/中的一种或数种。在以往的版本中,Google 官方推荐使用的是 drawable-hdpi/这样文件夹,但这是目前最新版本的改动,将不同分辨率的图片归到了 mipmap/文件夹中。

如果需要访问文件名、修改时间等文件的原始属性,则需要把文件存放在 assets/中,而不是 res/raw/中。

SVG = Scalable Vector Graphics (可缩放矢量图形),下同。

7

毕业设计

本语言的函数或者类那样需要绑定操作,大量节省代码。

2.2.2 Lua 插件

Lua是1993年诞生的一款脚本语言,发展至今仍非常流行,目前最新版本是5.3.2, 近些年基本没有太大改变,在各个平台都有相应的解释器可以整合到应用程序中。

Android系统下的Lua解释器主要有2款,一款是AndroLua,另一款是LuaJIT。第一 款主要用于脚本的执行,但是因其不包含JIT,脚本的运行效率比较低,但是优势是轻 便;第二款主要集成在游戏引擎中,用来优化游戏脚本的运行,重复运行脚本的话效率 很高,但是体积很大。

综合功能需求和移植的难易程度,本设计采用了AndroLua解释器来运行Lua脚本。

2.2.3插件开发套件

由于设计的插件是采用思路是:插件主体类继承自插件基础类、额外功能的使用接 口,所以开发的时候需要引用应用程序的主体类,因此编译插件时需要将某些模块加入 引用路径。主要的解决方案有如下几种:

1.用Android Studio载入整个项目,然后在项目中创建插件类,编译并导出成DEX 插件,这样做的前提是项目必须完全开源。

2.提取出最小的基础类集合用于开发插件,然后在项目里建立插件类,编译导出 成DEX插件。

3.将最小基础类集合编译导出成JAR包,用于插件开发及编译时的引用,然后编写 一套批处理脚本将最小基础类库导入到编译路径中,或者建立好IDE的项目,编 译DEX插件。

4.将最小基础类集合编译成库,发布到Maven等开源托管平台中托管,同时供应用 程序本体和开发插件的环境使用,这样的好处是只要维护一份插件开发包即可。

由于代码还没有完全开放,目前初版采取的插件开发策略是第1种,插件的开发全 部是是自己完成,具体步骤如下:

1.由Android Studio导出*.class文件;

2.通过指令将*.class文件打包成JAR包

jar cvf org.mewx.projectprpr.plugin.builtin.XsDmzj.jar ./org

3.将JAR包转换为为DEX文件

dx.bat --dex --output=org.mewx.projectprpr.plugin.builtin.XsDmzj.dex

该指令的*.class 文件在./org 文件夹中,由于 Jar 包转换成 DEX 插件时需要保证原始类的结构,例如:org.mewx. projectprpr.plugin.builtin.XsDmzj 类的*.class 文件路径须为 org/mewx/projectprpr/plugin/builtin/XsDmzj. class。

该工具在 ADT 的 build-tools/(version)目录下。

8

2插件式架构安卓阅读器的关键技术

org.mewx.projectprpr.plugin.builtin.XsDmzj.jar

4.存放在手机中本应用的目录中即可运行时自动加载(如图2.2.3-1所示):

图2.2.3-1 插件存放目录

由上述指令生成的DEX插件直接可以由应用程序读取并运行,而且有一个好处:无论 是Debug版还是Release版,均可以正常运行。

2.3 Material Design

Material Design是Google公司在其2014年的I/O大会上公布的一套设计规范。 其核心思想就是把物理世界的体验带进屏幕,去掉现实中的杂质和随机性,保留其

最原始纯净的形态、空间关系、变化与过渡,配合虚拟世界的灵活特性,还原最贴近真 实的体验,达到简洁与直观的效果。

从Android 5.0版本开始全面使用Material Design设计风格,开发者对于Material Design的态度也是十分狂热,2015年初一时涌现出大量Material Design风格的手机应 用。由于该设计规范严格细致,若所有Android应用都严格遵循该规范设计并制作,那 么Android平台的所有应用界面都将非常统一而美观。

2.3.1设计规范

Material Design的规范相当精致,而且到目前为止还在不断完善中。 其最主要的设计规范如下:

1.材质

Material Design中,最重要的信息载体就是纸片。纸片可以层叠、合并、 分离,且拥有现实中的厚度、惯性和反馈效果,能够自由伸展变形,同时拥有 液体的一些特性。除此之外,Material Design还引入了Z轴(高度)的概念,Z 轴垂直于屏幕,用来表现元素的层叠关系。Z值越高,元素离界面底面越远,投 影越重。

2.动画

Material Design非常重视动画效果,它反复强调:动画不只是装饰,它具

Material Design 目前还没有中文的官方译名,较为常见的网络译法有:材料设计、材质设计。

9

毕业设计

有含义,能表达元素、界面之间的关系,具备功能上的作用。典型的动画有: 加速减速运行、水波反馈、转场效果、图标的细节动画。这些动画在官方网站 上都有详细的规范说明,但是实际开发的时候实现起来还比较困难。

3.颜色

Material Design规定颜色不宜过多,选取一种主色、一种辅助色(非必需), 在此基础上进行明度、饱和度变化,构成一套配色方案。Google官方同样给出 了基础色的色板供开发者选取,并推荐开发者选取色板中权重为500的颜色作为 应用界面的主色。

4.图标

Material Design建议桌面图标模仿现实中的折纸效果,通过扁平色彩表现 空间和光影;按钮的图标则可以直接从海量的图标库中选取,提供的SVG图片可 以直接导出成标准的XML图形文件。更多细节可以参考Google官方介绍,但要注 意避免如下几点:

不要给彩色元素加投影;

层叠不要超过两层;

折角不要放在左上角;

带投影的元素要完整展现,不能被图标边缘裁剪;

如果有折痕,放在图片中央,并且最多只有一条;

带折叠效果的图标,表面不要有图案;

不能透视、弯曲。

5.布局

Material Design规定栅格系统的最小单位是8dp,一切距离、尺寸都应 该是8dp的整数倍。

如下是官方给出的一些常见的尺寸与距离:

系统状态栏高度:24dp

底部导航栏高度:48dp

用户头像尺寸:64×64dp / 40×40dp

小图标点击区域:48×48dp

侧边菜单到屏幕右边的距离:56dp

卡片间距:8dp

留白距离:16dp

屏幕左右对齐基线:16dp

DP = Density-independent Pixel (与设备无关的像素),下同。

10

2插件式架构安卓阅读器的关键技术

文字左侧对齐基线:72dp

需要格外注意的是56dp,许多尺寸可变的控件,比如对话框、菜单等,宽 度都建议按56dp的整数倍来设计。

2.3.2兼容控件

为了减轻开发者的负担,以及让尽可能多的设备可以体验到Material Design风格 的手机应用,Google官方从2014年起一直在不断推出新的Material Design的兼容组件, 加之开源社区中众多的开发者协作建立了很多兼容库,在主流机型实现Material Design 风格的应用已经变得十分容易。

官方的组件主要有:

com.android.support:support-v4 包含了Toolbar等控件;

com.android.support:appcompat-v7 包含了向下兼容的Activity基类;

com.android.support:design 包含了Material Design的资源文件;

com.android.support:cardview-v7 包含Material Design的卡片视图;

com.android.support:recyclerview-v7 全新、高效的列表视图。 其他开源组件有很多,以下是本项目用到的第三方组件:

org.adw.library:discrete-seekbar 带加速度的进度条控件。

com.nononsenseapps:filepicker Material Design文件选取器。

com.github.afollestad.material-dialogs 强大的对话框库。

2.4Gradle 构建

Gradle是一个基于JVM的构建工具,它相比于以往Android的构建方式,提供了:可 以切换的、基于约定的构建框架,多工程构建的支持,对于Maven和Ivy仓库的支持,以 及可以使用同样是JVM语言的Groovy编写构建脚本。

以往使用Eclipse+ADT开发Android会遇到一个问题,那就是引入第三方的JAR包时 需要首先将其下载到本地,然后一步一步导入到项目中。

而现在使用Gradle构建可以直接通过一行指令完成整个导入外部代码的操作,其基 于的是像Maven这样的开源代码库托管网站,而且不仅支持JAR包,还支持AAR等格式的 代码包,大大简化了开发的操作,也简化了项目的整体结构。

2.5代码版本控制

目前主流的代码版本控制工具有两款:SVN和Git。最主流的还是Git,因为其不仅 可以离线提交代码,还可以进行分支管理。

如果项目中需要引用一个开源的库,比如Android Volley(这是一个网络通信库, 它原生不支持byte[]类型的返回值),我们需要让它支持byte[]类型的返回值,那么就

11

毕业设计

需要对这个库源代码的某些部分进行修改,但是又想保证该库紧跟官方的最新版本,所 以需要能同步更新官方的库的功能。

如果使用SVN,如果不手动拉取官方库的代码,则需要你就把你的修改提交到官 方源去(基本上是不可能的)。

使用Git时,我们可以拉取一个代码库,新建一个分支保持私有修改,官方库有 更新随时拉去到库的主分支中即可。

本次设计采用Git本地建库,服务器作为备份的开发方式,使用的是Coding.net的 私有库托管代码的服务,不仅代码可以闭源,还可以预防由于意外情况导致的代码丢失 的悲剧发生。

12

3插件式架构安卓阅读器的设计与分析

3插件式架构安卓阅读器的设计与分析

构建一个手机应用实际上就相当于构建一个完备的系统,在正式构建之前首先要进 行设计。一个优良的设计可以尽可能写出耦合度低、易于扩展和重构的代码,对于添加 功能和改进结构都提供了非常大的便捷。因为软件的更新是不可避免的,无论何种设计, 最终都会需要改动。

在设计的方面,有一种错误叫过度设计,也就是俗称的“想太多”,很多内容不需 要考虑的却考虑进去了,导致系统设计的模块过于繁多,工作量增加太多。对应的另一 种错误叫做设计不足,实际构建系统的时候会发现某些功能无法很容易地编写完成,于 是需要进行一定范围内的重构,然后增加新的模块。

本项目在开始编码前首先确定了编写原则,全部使用UTF-8无BOM编码的源代码文 件,注释和代码均使用中文完成,且要支持i18n的特性。基于这些原则制作了思维导图如图3-1是思维导图的阅读器局部(和代码一样全英文书写):

图3-1 思维导图局部(阅读器模块)

可以看到上面这个思维导图就属于过度设计,预先设计了特别多的功能,导致系统 功能过于强大,代码也不容易编写,需要考虑整体的因素太多。图中两个带阴影的部分

BOM = Byte Order Mark (字符顺序标识),下同。标准的 UTF-8 文件的 BOM 应该是 0xEF 0xBB 0xBF,无 BOM 的 UTF-8 文件即不包含这三个前导字节。

注:本次思维导图绘制工具为 Mindjet MindManager 2016。

13

毕业设计

是需要提取出API供其他模块调用的。(注:图3-1中未包含其他模块的内容。)

按照设计规范,在项目伊始应当建立好UML图示作为详细设计。但是由于项目过于 庞大,UML的制作代价过于庞大,最终实际操作时还是采用了思维导图进行概要设计。 另一方面, UML的主要功能是表示类的建模,通过将具体的API或其他方法在代码中完 成空函数定义的方式建模也是可行的,而且写在代码中更加便捷,本项目的详细设计采 用的正是这种方式。

3.1插件式架构安卓阅读器概述

本设计主要是对目前市面上阅读应用在一定程度上的创新和改进。

目前网络上的小说等阅读资源大多没有经过整合,而且这些阅读资源属于版权敏感 区域,所以这方面至今还没有一个良好整合的平台。本设计通过模拟浏览器的方式对目 前现有的各种阅读器做了如下改进:

1.聚合资源

将各大在线阅读网站的阅读资源以插件库的形式整合在应用中,一方面增

加了阅读的内容来源,具有整合功能,另一方面可以很方便地更新。因为插件 是一种非常轻巧、便捷的形式,更新插件的时候只需要更新一个KB级的文件即 可,不必像大多数软件更新一样每次都安装新下载的安装包,那样非常费时费 力,并且对于用户很不友好。

2.通用架构

将所有资源的请求方式抽象成通用方法,在应用程序中提供的各类插件接

口中顺利绑定调用。根据Android的平台特性决定采用多种插件引擎,首当其冲 的就是DalvikVM自带的DEX插件加载器,效率和通用性都是最高的;另一种是使 用轻量级脚本语言作为插件,在脚本语言中定义相关的方法,也可以使用开放 的功能库,再由应用程序绑定插件里的方法,在运行时调用运行。

3.阅读体验改进

目前的阅读器几乎没有提供竖向阅读的功能,因为Android原生就不支持竖 排文本,所以目前没有看到主流阅读器提供竖排功能。但是很多阅读材料,例 如繁体中文和日语的书籍都是竖向排版,并且是从右向左阅读。还有更多的体 验上的改进,例如用户可以自行组建本地书籍等。还可以设定各种恼人的推送, 更新提示,允许用户开关统计功能,高度可定制化才会更受用户的欢迎。

4.支持云同步

本地的阅读书籍和进度都可以通过国内外各大云盘的开放API进行上传备

UML = Unified Modeling Language (统一建模语言),下同。

14

3插件式架构安卓阅读器的设计与分析

份、下载恢复。同样也应提供更加极客的方式——允许个人自建私人服务器进 行云同步,这部分并没有太多难点,应用本身只需要支持相关的通讯协议(例 如:SCP)即可做到“私有云同步”。

3.2阅读器的界面设计

本设计的界面遵循Material Design设计规范,力图将画面的材质感做到最好,并 且全面采用开源社区中效果最好的控件。

界面设计主要分为如下几步:

第一步,需要将界面的专场流程设计完,转场动画可以在后期添加; 第二步,构思界面的布局,使用何种控件、何种图标都需要准备就绪;

第三步,编写Android项目中的XML布局代码,将资源转换成(如:SVG转XML)或切 成(如:标量图切成适配不同屏幕大小的mipmap图片)Android所识别的资源格式。

3.2.1页面流程逻辑设计

云端管理

词典管理

图3.2-1 页面流程逻辑

如图2.3-1所示为本项目的页面流程逻辑

首先是主界面,主界面需要提供主要功能的导航,让用户直接到达应用所提供的某 一类功能,本设计主界面包括5个部分:本地书架、插件中心、词典管理、云端管理和 设置界面。

本地书架界面供了本地所有书籍的快速访问,由于本地书架包括的书有通过数据源 从网络上缓存的小说和其他本地各种格式的可阅读文件,所以这部分的设计与数据源插 件打通了,打通的部分即为阅读界面。

插件中心界面则展现为插件的大类,然后点进每个大类可以看见该类的具体插件列 表,插件列表会显示一些基本的插件信息,也会从开源项目中拉去最新的插件信息和已 有的插件列表进行比对,可以允许用户更新插件。

注:其中带有渐变阴影的部分是代码中已经实现的部分,未加阴影的部分是已经设计但是未完全实现。

15

毕业设计

词典管理界面的设计主要是由于阅读经常会遇到不懂的词,所以基本上词典功能成 了阅读软件的标配功能,本应用的设计也不例外。

云端管理界面需要提供云端备份的进度展示以及云端小说同步的配置,还要能提供 云盘的文件访问功能,因为能存就要能取。

设置界面是整个应用的通用设置,例如语言切换、清理缓存、关于本应用等信息。 其次是小说列表界面,它主要是数据源插件中某一个插件点进去首先加载的页面, 因为每一个阅读资讯站都会有最近更新的条目,所以点开一个数据源插件,首先看到一

个共有的内容比较符合通用设计,而且最新的内容基本上也是所有网站所需要展示的。

分类列表界面主要是针对大多数阅读网站对自己的阅读资料分了类,用户也会对分 类信息很感兴趣,所以可以单独请求某个分类的小说列表。

搜索小说界面也是小说网站所必须的,因为海量数据一个一个肉眼寻找还是非常难 的,所以无论是什么阅读站,几乎都提供了搜索功能,这算是阅读资源网站的一个共同 点。

登录网站界面主要是为了某些用户可以访问到自己的付费内容,有些阅读网站提供 的是部分免费阅读,用户可以在网站上选择付费功能,然后用自己的账户登录数据源, 这样可以请求到该用户所独有的数据资源,增强本应用的通用性。

之后是小说详情界面,它是从小说列表中点击进入的,无论是最新小说列表、分类 小说列表还是搜索结果的小说列表,其点击产生的请求都会转入小说详情界面,这个界 面主要是展示了小说具体的信息,包括更加详细的介绍以及章节列表。

章节界面主要是为了给小说的阅读跳转添加二级界面,一般来说小说由“卷”到下 级的“章”,章节界面主要是从小说详情界面的“卷列表”跳转到本界面的“章列表”。 增加了一级菜单,就让小说详情节目显得不是那么复杂了。

最后,阅读器界面是整个应用中最大的模块了,因为它需要同时能处理网络数据源 凶啊说的请求以及本地书载入的请求,对于本界面的兼容设计很重要。由于最开始设计 的有一项功能是自由组书,也即将本地的阅读文件组成一个整的阅读包,所以该界面设 计的时候考虑到了上下文跳转的问题,允许卷内各章之间的跳转。

查看大图界面也是一个通用界面,可以允许放大查看图片,最主要的是要能够防止 内存溢出的BUG,设计的初衷是能够查看阅读资源中的插图、小说的封面,以及一些图 片的预览等。

3.2.2界面布局设计和资源的准备

同样可以参考图3.2-1,对于每一个界面都不可或缺地要考虑技术实现的方式,与

注:因为 Android 显示图片先要将图片解码到 bitmap 的格式,于是会导致点阵在内存中的存储太占空间,对于 Android 的默认图片空间 ImageView 载入稍大一点的图片就会导致应用崩溃。

16

3插件式架构安卓阅读器的设计与分析

技术实现相违背的设计是非常失败的设计。因为每个软件或者硬件平台都有其固有特 性,如果违背平台的特性设计应用,实现出来也是违反用户使用习惯的,毕竟让用户卸 载软件比让用户习惯软件要容易得多。

对于主界面,首先要考虑的就是左侧的导航界面切换选项卡的时候应当进行的操 作,Google官方推荐使用Fragment控件作为容器,但是实际操作时Fragment会造成大量 的代码冗余。因为使用Fragment的时候需要为每个Fragment创建一个单独的类,切换 Fragment的时候需要销毁当前的Fragment并创建新的Fragment,其性能非常高,但是对 于布局有很大影响。如图3.2.2-1所示,一个界面可以分成大、中、小三种分割Fragment 的方式:采取小的Fragment代码较为简便,但是局限比较大,父级的Activity需要增加 许都接口供改Fragment调用;采取中等的Fragment则代码比较复杂,因为每一个 Fragment都需要维护一个Toolbar实例,而且Toolbar在每次切换的时候都需要销毁再创 建,十分浪费资源;采取大的Fragment是唯一一种可以完美让顶部状态栏和底部导航栏 透明的布局方式,但是这种方式和中等的Fragment一样,在Fragment中需要维护Toolbar 实例,效率过于低下。所以主界面最后经过抽象,决定采用主界面中布局RecyclerView 的方式,这利用的是RecyclerView的特性——可以显示任何一种列表布局(如ListView 和GridView)。

图3.2.2-1 Fragment框架介绍

本地书架、插件中心、词典管理可以很容易地做到,因为主界面中设置了 RecyclerView,每次需要切换的时候只要更新RecyclerView的适配器Adapter即可。

云端管理和设置界面使用的是Android的标准做法——弹出新的Activity,按照 Google官方的说明,左侧的导航菜单中只有平级的内容不需要弹出新的Activity,其他 的菜单选项均要弹出新的Activity表明内容之间的关系。

小说列表界面、分类列表界面、搜索结果界面都是带有RecyclerView的Activity, 比较容易做到的就是他们可以共用一个适配器,只需要填入不同的内容即可复用全部代

17

毕业设计

码,界面设计时也做到了重用。还有一个额外的特性,由于小说列表作为数据源的起始 页面,有些数据源的内容需要用户登录才有权限访问,所以从每个小说列表页面就需要 存储好于数据源网站的通信认证凭据,还有用户的用户名、密码等信息都需要加密保存 在本应用的文件夹中。

小说详情界面设计的是一种比较复杂的布局,但设计时考虑到设备的特性,所以设 计成了线性的页面,因为元素的类型太过于复杂,采取了一种动态布局——在代码中生 成视图动态添加到界面中。这种做法的实现方法是先定义一个基础的视图(本项目中是 一个配置好参数的CardView),然后使用inflator的功能将该视图载入到对象中,接着 改变相关的文字即可直接添加到整个线性布局中。

章节界面和小说阅读界面比较类似,使用的代码也很相近,主要是章节界面点击的 功能不一样。在设计中章节界面可以点击弹出不同的选项,例如导出本章、分享本章之 类的功能。

阅读器界面是设计的核心,因为阅读功能比较注重用户体验,而且这是与其他界面 不同的,该界面要求用户可以全屏阅读。在一定程度上简化开发的工作量,并且能保证 用户的体验,本次设计中将状态栏设置成25%的透明色,让用户在阅读时可以看到状态 栏的电量、时间等信息(如图3.2.2-2)。

图3.2.2-2 阅读界面状态栏效果图

查看大图界面首要考虑的就是内存溢出的问题,Android作为移动设备,所有的运 算资源都很有限,必须在考虑技术实现的问题上设计。首先就是考虑将查看大图的部分 和阅读器剥离,形成一个单独的模块可以供所有的Activity调用,这部分通过传入URI实现。在该界面中实用的图片控件是一个开源社区中应用很广泛、性能经过严格优化的 图片缩放的控件——com.davemorrissey.labs:subsampling-scale-image-view,使用 该控件可以自动处理用户的放大缩小拖动的手势,功能很强大可以分块加载节约内存, 不需要进行太多额外的编码工作。

设置界面考虑到工作量和界面风格统一的问题,采用Google官方兼容设置界面的基 类——AppCompatPreferenceActivity。使用该类不需要编写太多代码即可完成和 Material Design风格相同的设置界面,其中设置项的配置全部写在menu目录中的XML文 件里。

注:Android 中,应用本身具有、且只有应用本身具有完全控制权的文件夹即为“应用的文件夹”,其他应用以及 使用者对该目录均没有任何访问权限。

URI = Uniform Resource Locator (同一资源定位器),下同。

18

3插件式架构安卓阅读器的设计与分析

3.2.3资源文件的转换与界面制作

应用界面开始实施时主要是2部分工序。

第一部分是相关资源的准备,例如图片等资料的整理和适配。

主要需要处理的资源就是图片的格式转换,图片需要从SVG格式转换成Android识别 的XML矢量图。例如原始的SVG文件是这样的:

<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24"> <path d="M0 0h24v24H0z" fill="none"/>

<path d=" M12 8V4l8 8-8 8v-4H4V8z "/> </svg>

由于SVG是一种开源的格式,Android从2015年开始发布了兼容SVG的库,让更多低 版本Android系统也能显示SVG图片,将SVG转换成Android的XML只需要修改一些特有的 定义字段即可:

<?xml version="1.0" encoding="utf-8"?>

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"

android:height="24dp">

<path

android:fillColor="#FFFFFF" android:pathData="M12 8V4l8 8-8 8v-4H4V8z" />

</vector>

其他的切图通常可以采用PxCook这款工具,它可以根据设计图自动导出各种屏幕规 格所需要的标量图,在使用导出的图片时,只需要将图片拖放进相应的mipmap目录,程 序在运行时自动会根据分辨率加载图片。

第二部分是布局的实际编写。

1.本地书架

这部分显示离线可以阅读的书籍,包括通过数据源插件缓存到本地的小说,

也包括本地添加到阅读列表中的书。界面效果图如3.2.3-1和图3.2.3-2所示, 所有本地有信息的书籍均显示在CardView中,然后通过在RecyclerView中将布 局管理器设置成GridLayoutManager可以实现GridView的效果。

19

毕业设计

2.插件中心

如图3.2.3-3所示,这部分和本地书架属于同一个Activity下的两种状态, 切换方式是通过选择左侧导航菜单(如图3.2.3-4)中的菜单。

这部分由于是本应用提供的功能,所以全部是硬编码的菜单,同时出于i18n 的原则,这部分引用的数组也做了多国语言的处理,即全部使用字符串的id标 识引用字符串:

<string-array name="plugin_center_items"> <item>@string/plugin_center_items_data_source</item> <item>@string/plugin_center_items_formats</item>

</string-array>

3.设置界面

20

3插件式架构安卓阅读器的设计与分析

效果见图3.2.3-5,采用的是系统自带的设置界面,和系统风格相同,这部 分界的制作通过配置XML格式的菜单即可,如"pref_general.xml"文件内容如 下:

<PreferenceScreen>

<ListPreference

android:defaultValue="1" android:entries="@array/setting_language_list" android:entryValues="@array/setting_language_list_return" android:key="language_list" android:positiveButtonText="@null" android:title="@string/setting_language" />

<Preference android:key="clear_cache" android:title="@string/setting_clear_cache" android:summary="@string/setting_clear_cache_explanation" />

<Preference

android:key="about" android:title="@string/setting_about"/>

</PreferenceScreen>

4.数据源插件

如图3.2.3-6所示,这部分虽然程序的逻辑比较复杂,但是界面逻辑很简单, 只需要显示所有可用的插件列表即可,这里采用的同样是Material Design的 CardView控件,而且通过嵌套使用RelativeLayout实现半透明的效果。

21

毕业设计

5.小说列表

如图3.2.3-7和图3.2.3-8,分别为第二个数据源和第一个数据源进入的小 说列表,可以看到不同的数据源提供了不同的内容,这里采用的策略就是抽取 公共部分:每个小说都有标题、作者,那么首先在布局中这定好这两项作为占 位符,如果数据源提供了额外的信息,在填充数据的时候会额外追加新的信息, 最大程度上地利用所有请求到的资源。

6.小说详情

图3.2.3-9是从小说列表跳转到的小说详情界面,和本地书架中缓存自数据 源插件的书跳转到的界面是一样的。在该界面中包括如下一些功能:添加到本 地书架和下载功能。

下载功能提供了4种具体的下载方式:

1.检查更新:更新小说信息和章节信息。

2.更新下载:本地存在文件则跳过下载,只下载本地不存在的文件。

3.覆盖下载:无论本地是否有文件,都覆盖下载一遍。

4.分卷下载:选择某一个卷进行更新下载(如图3.2.3-10)。

下载时会弹出一个对话框(如图3.2.3-11所示),这样比较好处理,后期如

果维护的话要创建一个Service来处理下载内容。

5.章节列表

如图3.2.3-12所示,章节列表主要是作为二级菜单供用户选择阅读的内容, 目前重用了小说详情界面的代码,但是性能欠佳,后期的维护中应当改成使用 RecyclerView管理章节列表。

22

3插件式架构安卓阅读器的设计与分析

6.阅读器界面

如图3.2.3-13所示,阅读器界面顶部是25%透明度的状态栏,用语显示电量 时间等信息,左下角显示的是当前的卷名,右下角是当前的阅读进度,如果点 击屏幕中央可以看到左上角的标题栏显示当前章节的名称

图3.2.3-14是阅读的翻页动画,点击屏幕左侧或者右侧都可以进行翻页, 同时也设计的了音量键的翻页功能,音量上下键分别对应向上翻页、向下翻页。

图3.2.3-15显示的是阅读器的菜单,单击阅读器中心部分即可呼出菜单以 及系统的导航栏,再次单机屏幕即可隐藏。菜单左上角是返回,标题栏显示的 是章节标题,右上角是查看图片的功能,如果当前页有图片点击可以查看图片 你的大图。底部是一系列的菜单功能区,从左向右依次是:白天/夜间模式切换、 快速跳转阅读进度、查找文本、更多设置(如图3.2.3-17)。

图3.2.3-16是切换的夜间模式,这里采用的算法可以瞬间切换夜间模式, 而不需要重新加载阅读器,其原理是点击按钮后直接通知阅读器的视图更改背 景,然后阅读器视图会通知父级的Activity刷新视图,直接完成了切换背景的 操作。

图3.2.3-17是更多功能的界面,除了前面的夜间模式、快速跳转、查找功 能外还有一些阅读器的细节设置功能:字体大小、行间距离、段间距离、段落 边距、自定义字体、自定义阅读背景。这部分的设置也是和夜间模式一样即时 生效,原理完全相同,是视图给按钮开放的调用接口。

一部作品一般是由很多本书构成的一个系列,每一本书叫做一卷,每一卷目录中的每一个条目叫做章节。

23

毕业设计

7.查看大图

如果页面中含有图片(如图3.2.3-18),点击右上角的菜单按钮即可进入查 看大图的界面(如图3.2.3-19),该界面和阅读界面风格是完全统一的,单机屏 幕可以呼出菜单,再次单击即可隐藏。

在查看大图界面中菜单有如下两个:顺时针旋转图片、图片另存为。图 3.2.3-20即为图片顺时针旋转90°并通过放大手势放大图片的结果。

3.3阅读器的功能设计

由前文图3-1可以看到,在初期设计中主要完成的就是功能的规划,和页面的划分, 下面对于目前已经完成的界面功能进行说明。

1.本地书架

在切换到本地书架界面的时候会在顶部Toolbar中显示一个添加本地书籍

24

3插件式架构安卓阅读器的设计与分析

的按钮,点击后会弹出文件选择页面,允许添加阅读文件到本地书架中,添加 完成后本地书架就会显示出该文件(如图3.2.3-2)。

长按某一本书可以弹出一个对话框,显示出更多的选项,如删除书籍、查 看属性等附加功能。

点击某一本书可以跳转到书籍详情页面或者直接进入阅读器。如果点击的 是从数据源插件中下载的书籍,会跳转到书籍详情界面,这部分不会再次联网, 使用的均是已缓存在本地的信息;如果点击的是本地添加的书籍,那么会直接 跳转到阅读界面。

2.插件中心

插件中心会列出当前可用的插件类型,目前版本设计的插件类别只有两种:

数据源插件、阅读格式插件。

点击不同的插件图标可以进入相应的插件列表。

3.设置页面

主界面进入的设置界面是用于对于整个应用程序的设定,目前开放的设定 选项有软件语言选择、清理缓存、关于应用。

软件语言选择包括4个选项:跟随系统、简体中文(如图3.3-1)、英文(如 图3.3-2)、繁体中文(如图3.3-3)。

清理缓存包括严格清理和粗略清理两种。严格清理会遍历每一个小说的文 本,所以未被引用的图片均会被删除;而粗略清理只会清理缓存的临时图片, 未被引用的小说插图则不会清理。

关于应用会弹出一个对话框,注明软件的版权信息和开源库的使用情况。

4.数据源插件页面

数据源插件页面显示的是某一个类别的插件列表,如图3.2.3-6所示,包含

25

毕业设计

了一个本地数据源插件、一个内置的数据源插件。该页面显示的是当前可用的 插件和云端可加载的插件,插件版本的更新也会在该页面显示出来。

长按插件会弹出一个对话框,可以禁用或者删除某个插件。

单击某个插件可以进入该插件的初始页面,一般来说数据源插件的初始页 面是最近更新的小说列表。

5.小说列表

小说列表页面展示了最近更新的数目,可以看到小说的基本信息,包括标

题、作者、封面等信息。

单击某本书则可以进入小说详情界面查看详细信息。

6.小说详情

小说详情会显示小说的详细信息,包括标题、作者、更新时间、详细介绍 等信息,在这些信息的下方会列出所有的卷名,点击卷名会进入该卷的章节列 表页面。

本页右上角提供了2个功能按钮,一个是加入本地书架,另一个是缓存本小 说至本地。在该页面加载时,这两个按钮都是无法使用的,因为加入本地书架 的原理是将本页显示的内容缓存至本地文件,而缓存小说至本地则需要先加入 本地书架。所以在本页面加载时点击这两个按钮会提示请等待页面加载完成。

7.章节列表

章节列表显示的是所点击卷下的所有章节,点击章节可以进入阅读界面。

8.阅读器界面

阅读器界面中拥有翻页动画,左右滑动手势可以滑动,音量键上下键也可

以翻页,点击屏幕左侧或右侧也可以翻页,翻页如果遇到章末可以进入下一章。 由于设计的功能是一本书一本书浏览,所以只能在卷内跳转,不能跨卷跳转。

阅读时点击屏幕中央可以呼出菜单,同时呼出导航栏,再次点击可以隐藏 导航栏和阅读器的菜单。阅读时通过菜单可以随时调整字体大小、段间距、行 间距、页边距、字体、背景以及快速跳转段落,执行效率非常高。

9.查看大图

查看大图页面支持常用的放大手势,并且可以旋转90°的整数倍。

和阅读器界面一样,单击图片可以呼出查看大图界面的菜单(如图 3.2.3-19),菜单上的两个功能分别为顺时针旋转90°、保存图片。

其中保存图片通过文件选取器选择目录后会弹出一个对话框要求输入图片 名称(如图3.3-4),输入后确定图片即保存在所选择的位置。

26

3插件式架构安卓阅读器的设计与分析

图3.3-4 查看大图保存图片

3.4阅读器的模块设计

现代编程方法提倡所有的系统都应该按照模块化设计的方案,模块与模块之间相互 独立,全部通过开放接口的方式互相通信。

本次毕业设计也是按照这样的原则进行编码的。每一个Activity之间都是相互独 立,有些Activity(如:数据源起始页面和小说详情页面)之间会有联系,按照原则也 是应当通过开放接口进行数据的访问。

这部分主要介绍本应用中一些比较大的模块。

3.4.1书架管理模块

本模块是一个静态全局模块,在应用启动时即会加载初始化,然后由初始的 Activity调用该模块提供的初始化接口,所有开放接口设计如下:

1. void loadAllBook()

载入全部书籍,这里会根据目录结构自动载入通过数据源下载的书籍,也 会载入本地导入的书籍。

2. List<BookshelfSaver> getBookList()

获取List类型的书目列表,包括所有来源的书籍均包含在返回值中,主要 用于显示整个书架的书列表。

3. void removeBook(@Nullable String dataSourceTag, String novelTag)

删除某本书,这里需要提供的参数是:数据源的标识和该书在该数据源中 所对应的标识。

4.void removeBookAt(int i)

这个方法用于书架发出的删除命令,因为书架中书的操作全部是以数组索

引作为标识的,传入索引id即可删除对应书籍。

27

毕业设计

5. void addLocalBookToBookshelf(@NonNull String fullPath)

向书架中添加本地书籍,传入参数是完整的文件路径,其中这个方法中调 用了一个侦测文件类型的模块。

6.void addToBookshelf(BookshelfSaver saver)

向书架中添加书籍,传入一个由BookshelfSaver数据结构表示的参数。

7.boolean inBookshelf(@Nullable String dataSourceTag, @NonNull String novelTag)

测试某一本书是否在当前的书架中,传入参数为数据源的标识和该书在该 数据源中所对应的标识。

未开放的内部接口只有一个:void saveAllLocalBookList(),用于保存导入到书 架中的本地书的列表信息。

3.4.2数据源插件模块

本模块和书架管理模块类似,在整个应用的运行周期只能存在一个实例, 首先也是 由初始的Activity调用该模块提供的初始化接口,所有开放接口设计如下:

1.void loadAllLocalDataSourcePlugin()

载入所有的本地数据源插件到该模块内部以供后期的访问和操作。

2.void loadAllCloudDataSourcePluginAsync()

异步载入所有云端的数据源插件到该模块内部。

在数据源插件版本号相同的情况下,云端的插件优先级默认是低于本地数

据源插件的,主要是考虑到开发者插件开发的便利;如果版本号不同,则高版 本号的优先。

3.NovelDataSourceBasic loadDataSourcePluginClassByTag(@NonNull String dataSourceTag)

首先需要初始化该模块,然后可以通过每一个插件的唯一标识符来获取该 插件所对应的基类形式——NovelDataSourceBasic,该基类已经定义了一个数 据源所需要的绝大部分接口。

如果需要使用未包含在该基类中的功能,则插件开发通过继承自该基类, 同时实现额外功能的接口做到,所以返回该基类是完全具有通用性的。

4.NovelDataSourceBasic loadDataSourcePluginClassByName(@NonNull String name)

根据类的完整名称获取该插件对应的基类形式。

例如:字符串"org.mewx.projectprpr.plugin.builtin.XsDmzj"。

5.NovelDataSourceBasic loadDataSourcePluginClassById(int index)

28

3插件式架构安卓阅读器的设计与分析

由于提供了获取插件列表的接口,所以可以根据插件信息在列表中的索引 来加载某一个插件对应的基类形式。

6.NovelDataSourceBasic loadDataSourcePluginClassByInfo(PluginInfo plug inInfo)

根据插件列表中某一个条目(表现形式是PluginInfo类)获取指定插件对 应的基类形式。

7.boolean checkDataSourcePluginAvailable(@NonNull String dataSourceTag)

检查某一个插件是否可用,主要是考虑到这样的使用场景:本地书架缓存 了某一本网络书籍,但是需要阅读时如果找不到对应的插件就很难处理,所以 每次打开本地缓存的网络书籍时会首先判断插件是否可用,可用的话才能继续 阅读。

8.int getLocalDataSourcePluginCount()

获取本地数据源插件的数目。

9.PluginInfo getLocalDataSourcePluginInfoById(int id)

通过id获取本地数据源插件的PluginInfo形式。

3.4.3阅读器模块

这部分整合成了一个特别大的模块,而且未来可以比较容易独立成包供各种软件引 用。这个模块与其他模块不同的是,它的所有文件均整合如下的包中:

org.mewx.projectprpr.reader

这部分主要包含如下组件:

1.阅读器Activity。提供了Activity上下文交互的载体,所有阅读器的组件均显 示在本Activity上。

2.阅读格式加载器。阅读格式插件继承自本加载器,将加载器打包入阅读器模块 有利于阅读格式的加载。

3.阅读器设置。这部分用于处理阅读器的设置参数,同样一个应用只能存在一个 该类的实体,而且是静态实体。阅读器初始化时会需要从该部分读取设置参数 以便绘制阅读界面内容。如果设置的参数和默认值一样,那么将不写入文件, 减少存档体积也能一定程度上提高加载速度。

4.滑动翻页功能组件。这部分是利用ViewGroup编写的阅读器滑动视图,同时也自 己编写了适配器,用于提高翻页的效率。和一般的阅读器不同,本阅读模块一 次会加载3个页面,即前一页、本页和下一页,能大幅提高运行效率和翻页动画 的流畅程度。因为在阅读时会预加载前一页和下一页,一旦用户执行了翻页操 作,会直接将计算好的页面显示出来。

29

毕业设计

5.阅读视图。这部分包括整个页面的视图和某些自控件的视图(如:设计中的电 量显示控件和时间显示控件),整个页面的视图也就是阅读时所看到的每一页的 界面。

6.分页算法。这部采用了动态分页的算法,运行时不分页,每次计算上一页(如 果有的话)、当前页和下一页(如果有的话)的第一个字符的句子id和句中字的 id以及最后一个字符的句子id和句中字的id,具体实现方法就是按照句子根据 屏幕的宽度向前向后移动游标测算这些id,最后绘制阅读视图时是将当前计算 得到的本页第一个字符和最后一个字符之间的所有内容绘制到屏幕中。

3.5阅读器的数据设计

由于应用开放给用户的自定义功能(如设置功能),所以需要将部分数据存储到手 机的存储器中,针对不同的数据本应用采取了不同的存储格式和策略。

3.5.1 JSON

JSON是一种轻量级的数据交换格式。由于其结构的固定性,被Android平台原生支 持,并且效率相对于XML的解析更高。

该格式的文件主要用于获取一些列表或者信息的比对查询。

例如软件更新时,服务端设立一个固定的文件:version_info.json,应用获取该 文件并解析就可以得到是否有新版本,如果有新版本可以弹出对话框。文件样例:

{

"version_name": "developing",

"update_time": "2016-05-25",

"size": "3354.55 KB",

"version_code": 1,

"md5": "bb36d5eaf5b9e60b123424e2c4c15773 ",

"download_url": "http://prpr.mewx.org/downloads/v1.apk"

}

如果需要获取插件列表,使用JSON也是不错的选择。如下的插件列表信息可以很方 便地被Android识别,而且借助GSON开源库可以直接将JSON文件转换成DalvikVM中的类 对象,大大简化了开发步骤。

[

{

JSON = JavaScript Programming Language,下同。

30

3插件式架构安卓阅读器的设计与分析

"name": "不可能的世界",

"tag": "8kana",

"author": "MewX",

"url": "http://m.8kana.com",

"version": "1",

"release": "2016/05/16"

},

{

"name": "动漫之家·轻小说",

"tag": "dmzjqxs",

"author": "MewX",

"url": "http://q.dmzj.com",

"version": "1",

"release": "2016/04/18"

}

]

3.5.2 Serializable

Serializable的中文释义,简单说就是为了保存在内存中的各种对象的状态,并且 可以把保存的对象状态再读出来。

一般应用场景是Activity之间的传递参数,可以把参数保存到Intent或者Bundle中 进行序列化,然后交由接收者解除序列化解析为对象。但是在本应用中,由于小说的卷 信息和章节信息太过复杂,如果先将信息编码保存到文件,使用时再读取文件解码效率 显得过于底下,所以存储在本地的章节信息实际上是Serializable类型的对象,写入文 件流时使用的是ObjectOutputStream,读取时用的是ObjectInputStream。

3.5.3数据摘要与数据编码

数据编码主要有2种大类:一种是可逆的,用于压缩或者将不可显示的内容转化为 可显示的内容;另一种是不可逆的,称之为数据摘要算法。

本应用在处理图片的文件名时使用的就是不可逆的SHA-1摘要算法,源信息是由多 个标识组合而成的字符串,主要是防止文件名重复。

在存储小说信息的时候采用的是base64编码,因为每一个字段都有可能含有和分割 符重复的字符,如果处理转义规则的话增加了更多的工作量,所以采用base64这种无损 编码。

31

毕业设计

4插件式架构安卓阅读器的具体设计

Android在系统诞生伊始就开始采用MVC的软件层次结构(如图4-1),这种软件设 计思路将业务逻辑、数据、界面显示分离,将业务逻辑聚集到一个部件里面,MVC在使 用时几乎感受不到,但是实际编码的时候代码结构非常清晰,便于维护。

在Android系统中,模型层适合做一些业务逻辑处理,比如数据库操作,网络请求, 耗时的算法,耗时的任务等都在模型层层处理;视图层则是显示应用处理的数据,XML 布局文件可以视为视图层,用于显示模型层的数据结果;Activity处理用户交互问题, 可以认为Activity控制层,读取视图层的数据,接收用户输入,并向模型发送数据处理 的请求。

图4-1 MVC和MVP对比图

但是Google最新的官方推荐,是希望开发者使用MVP架构,其与MVC的区别可以由 图4-1看出,模型层不能改变视图层的内容了,而是需要由展示器作为中间件,这样各 部分就相互独立不会像MVC那样相互依赖、关系复杂。

MVP会使编码量在一定程度上提高,因为展示器和视图一一对应,而且对于MVP的结 构还不是很熟悉,所以本设计主要仍使用MVC架构。

下面对于项目的几个主要结构进行详述。

4.1项目配置

本次设计采用了Gradle构建工具,在应用的Gradle的配置文件build.gradle中,首 先定义应用的版本和使用SDK的版本:

MVC = Model – View – Controler (模型-视图-控制器),下同。

MVP = Model – View – Presenter (模型-视图-展示器),下同。

32

4插件式架构安卓阅读器的具体设计

compileSdkVersion 23 buildToolsVersion "23.0.3"

defaultConfig {

applicationId "org.mewx.projectprpr" minSdkVersion 15 targetSdkVersion 23

versionCode 1

versionName "under developing"

}

然后在下方的dependencies字段下将所有的依赖库引用: dependencies {

compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12'

compile 'com.android.support:support-v4:23.3.0' compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:design:23.3.0' compile 'com.android.support:cardview-v7:23.3.0' compile 'com.android.support:recyclerview-v7:23.3.0'

// third party

compile project(':andro-lua') compile('com.github.afollestad.material-dialogs:commons:0.8.5.8') {

transitive = true

}

compile 'com.squareup.okhttp3:okhttp:3.2.0'

compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0' compile 'com.facebook.fresco:fresco:0.9.0'

compile 'com.facebook.fresco:imagepipeline-okhttp:0.9.0'

compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.5.0' compile 'org.adw.library:discrete-seekbar:1.0.0'

compile 'com.nononsenseapps:filepicker:2.5.2'

}

然后执行构建,Android Studio会将所有的代码和所有的依赖项全部构建成一个完

33

毕业设计

整的Android应用程序。

4.2主界面

模型:activity/adapter/BookshelfAdapter.java

视图:layout/app_bar_main.xml

控制器:activity/MainActivity.java

这部分模型主要处理书架的相关信息,将模型所代表的信息数据显示在视图所表示 的界面中,控制器用来具体初始化视图,以及将视图和模型绑定。

4.3左侧菜单栏

视图:layout/activity_main.xml

控制器:activity/MainActivity.java

由于左侧菜单栏使用的是系统控件DrawerLayout,所以这部分不需要模型,系统完 成了所有内容的绘制,内容部分是存储在menu/activity_main_drawer.xml中。用户和 视图的交互全部在控制器中处理和分派。

4.4数据源插件界面

模型:activity/adapter/PluginCenterItem.java

视图:layout/activity_plugin_center_data_source.xml

控制器:activity/PluginCenterDataSourceActivity.java

这部分显示了所有的插件列表,借助DataSourcePluginManager载入数据,然后通 过模型处理获取的数据显示在视图中,控制器负责显示内容的用户交互处理。

4.5数据源初始界面

模型:activity/adapter/NetNovelListAdapter.java

视图:layout/activity_data_source_item_initial.xml

控制器:activity/DataSourceItemInitialActivity.java

这部分模型用于处理小说的列表,将模型所代表的信息数据显示在视图所表示的界 面中,控制器用来具体初始化视图的样式,以及将视图与模型进行绑定。

4.6小说详情界面

模型:activity/DataSourceItemDetailActivity.java

视图:layout/activity_data_source_item_detail.xml

控制器:activity/DataSourceItemDetailActivity.java

这部分由于布局不适合使用标准的控制器(可参考3.2.2),而且也没有重用的需求,

34

4插件式架构安卓阅读器的具体设计

所以将模型和控制器都安排在控制器中,但是模型和控制器的方法是独立的。

4.7小说章节界面

模型:activity/DataSourceItemChapterActivity.java

视图:layout/activity_data_source_item_chapter.xml

控制器:activity/DataSourceItemChapterActivity.java

这部分结构十分简单,操作时直接将模型和控制器都安排在控制器中,但是模型和 控制器的方法是独立的,只是存在形式由不同类变成了不同函数。

4.8阅读器界面

模型:reader/slider/SlidingAdapter.java

视图:layout/layout_reader_swipe_page.xml

控制器:reader/activity/ReaderActivityV1.java

这部分通过用SlidingAdapter作为模型载入ReaderPageViewBasic,然后通过模型

处理获取的数据显示在视图中,控制器则用来处理用户的所有交互。

4.9查看大图界面

模型:com/davemorrissey/labs/subscaleview/SubsamplingScaleImageView.java

视图:layout/layout_view_image_detail.xml

控制器:activity/ViewImageDetailActivity.java

这部分使用的是开源的第三方库,库直接接管了模型处理问题,所以这部分所需要 做的就是通过控制器传入图片URI,然后交由三方库处理显示到视图中。

4.10设置界面

模型:activity/SettingsActivity.java

控制器:activity/SettingsActivity.java

这部分视图、模型和主要的控制器完全由系统接管,所有的设置项均配置在 xml/pref_general.xml中,自己写的部分控制器处理用户的点击事件即可。

4.11数据源插件架构

模型:plugin/NovelDataSourceBasic.java

控制器:plugin/JavaCallJavaClass.java

这部分是一个业务逻辑模块,插件全部继承自抽象类NovelDataSourceBasic,插件 载入器JavaCallJavaClass载入插件类,并转换成模型类返回给应用使用。

35

毕业设计

5插件式架构安卓阅读器的系统测试

5.1代码质量分析

现代开发迭代中,有一个很重要的步骤就是提交代码后的代码审查,但是由于本项 目是个人开发,没有代码审查者,所以为了控制代码质量,采用了代码质量分析工具, 起到一定程度山的代码审查。

使用Coding.net的代码质量分析工具可以很容易分析出代码存在的问题,有些是自 己编辑时的疏忽,而另一些则是违反了编程规范,比如Java中修饰关键词的顺序应当是 这样的:

public static final String key = "string";

如果这6部分顺序被打乱,则代码审查时会显示出来。

图5.1-1 Coding.net质量管理界面

如图5.1-1所示,顶部显示的是Git提交的版本号及运行代码分析的时间,左上角显 示的是代码的一些统计指标。由于目前Coding.net的质量管理系统对于Android项目只 能分析Java代码,所以XML文件的质量没能进行质量分析。右边“复杂度”显示的是整 个项目所有文件复杂度的总和,一般来说复杂度越低越好,但是由于其使用的是求和算 法,所以随着代码的增加,整体上来说其复杂度是增加的。

代码审查特别好的地方就在于,能看到自己代码与质量检测标准相比有哪些不足。 比如图5.1-1所示的代码重复率,这部分就提示开发者,某些代码可以抽象成函数,或

36