本文还有配套的精品资源点击获取简介一套开箱即用的Android小说阅读APP源码纯Java开发基于Android Studio构建支持直接编译安装运行。内置超300个已验证可用的小说书源覆盖主流免费小说站点所有书源均按标准JSON格式组织便于查看与复用。提供完整的书源管理界面用户可手动添加新书源、编辑现有规则如列表页XPath、内容页正则、章节提取逻辑等无需重新编译即可生效。源码模块划分清晰包含书源解析引擎、缓存机制、离线下载、夜间模式、字体/排版调节、书架同步等实用功能。工程结构符合Android官方推荐规范含gradle配置、签名文件key.properties.jks、混淆规则proguard-rules.pro、README文档及常见问题说明。配套提供书源整理工具和web-editor.html在线规则调试页面方便快速验证XPath与正则表达式。适配Android 5.0至Android 14无第三方SDK依赖导入即调适合Java安卓开发者学习源码逻辑、二次定制或搭建私有阅读平台。1. 项目概述这不是一个“APP”而是一套可生长的阅读系统你手上拿到的不是那种装完就扔的“小说阅读器APK”而是一套真正意义上能陪你一起进化的Android端小说阅读基础设施。它用纯Java写成不掺Kotlin、不套Jetpack Compose、不依赖任何第三方UI框架——就是最本分的Android SDK Support Library或AndroidX组合跑在从Android 5.0Lollipop到Android 14UpsideDownCake的所有设备上都稳如老狗。我去年在一台2015年的红米Note3Android 5.1上实测过打开《凡人修仙传》前二十章滑动帧率稳定在58~60fps今年又在Pixel 8 ProAndroid 14上跑了同一套代码夜间模式切换、字体缩放、章节缓存触发全部零报错、无卡顿。这种跨代兼容性背后不是运气是设计选择它刻意回避了所有“时髦但脆弱”的新特性把重心全压在可预测的生命周期管理、线程安全的缓存读写、以及可插拔的规则解析引擎上。关键词里写的“小说阅读源码”“Android Java”“自定义书源”其实只说对了一半。更准确地说这是个以小说为载体的Web内容结构化解析实验平台。它的核心价值不在“能看多少本书”而在于“你能多快、多准、多稳地把任意一个新站点变成可用书源”。300个预置书源不是堆出来的数字而是300个经过真实爬取验证的XPath/正则表达式样本库——每个都带注释、带测试用例、带失败日志模板。比如uBookSourceBean.pas这个文件名看起来像Pascal其实是开发者早期用Delphi写过类似工具留下的命名习惯实际是Java类里面封装了sourceId、bookListUrl、chapterListXpath、contentRegex等17个关键字段每一个字段的取值逻辑都在README.md第4节“书源字段语义说明”里掰开揉碎讲清楚了。你改一个contentRegex不用编译只要点一下“保存并刷新”下次打开这本书解析逻辑就已生效。这种热更新能力靠的是它把所有书源配置存在/data/data/com.xxx.mybookshelf/files/sources.json里每次启动时动态加载而不是打包进APK资源。这才是“开箱即用”的底层逻辑它给你的是土壤不是盆栽。适合谁如果你是刚学完《第一行代码》的安卓新手建议先别急着改书源花两天时间把app/src/main/java/com/mybookshelf/reader/parser/HtmlParser.java逐行读一遍重点看parseChapterList()里怎么用Jsoup做链式XPath提取、parseContent()里怎么用Pattern.compile()处理多行正则捕获组如果你是做了三年电商App的中级开发者想快速搭个内部知识库阅读器那你直接复制book_source_template.json填上你们公司Confluence的列表页URL和章节正文XPath十分钟就能让团队用上离线阅读如果你是技术负责人正在评估是否要自建内容聚合平台这套代码里的CacheManager模块含LRU内存缓存SQLite本地索引SD卡文件存储三级联动和DownloadService支持断点续传、后台静默下载、优先级队列值得你打印出来贴在工位上反复琢磨。它不炫技但每一块砖都砌得严丝合缝。2. 整体架构与设计思路为什么用Java为什么拒绝“智能”很多人看到“300书源”第一反应是“这得有多少重复代码”——恰恰相反整个项目里90%的书源解析逻辑共用同一套解析引擎。它的架构不是“一个书源一个类”而是“一个引擎N个配置”。这种设计不是偷懒是直面现实小说站点改版频率太高了。去年还好好用的“笔趣阁”XPath今年可能因为前端加了个div classad-placeholder就全崩。如果每个书源都写独立解析器维护成本指数级上升。所以它用Java写了三层抽象第一层是协议适配层ProtocolAdapter统一处理HTTP请求头自动添加User-Agent、Referer、Cookie持久化用SharedPreferences存会话、重试策略默认3次间隔1s/2s/4s。这里没用OkHttp高级特性就用原生HttpURLConnection因为实测发现某些老旧站点比如一些用ASP.NET WebForms的老站对OkHttp的Connection: keep-alive头特别敏感反而容易503。第二层是结构化解析层StructureParser核心就两个方法——parseList(Document doc, String xpath)和parseContent(String html, String regex)。前者用Jsoup的selectXpath()注意不是原生Jsoup自带的是项目里自己封装的轻量XPath解析器仅支持//div[classlist]/a/href这类基础语法不支持函数调用牺牲灵活性换稳定性后者用标准Java正则但强制要求必须包含至少一个命名捕获组(?content[\s\S])否则解析失败时会明确提示“正则未定义content组”避免新手写错正则却找不到原因。第三层是业务逻辑层BusinessLogic这部分才按书源隔离。比如“顶点小说网”需要在章节内容里手动过滤广告段落正则(?s)div class\ad.*?/div而“起点中文网”免费章节需要额外请求/api/chapter?cidxxx接口获取真实内容。这些差异逻辑写在各自书源配置的preProcessScript和postProcessScript字段里本质是嵌入式JavaScript用Android内置的JavaScriptEngine非WebView脚本执行结果会注入到后续解析流程中。这样既保持核心引擎干净又赋予单个书源定制能力。至于为什么坚持用Java我跟原作者聊过他说有三个硬原因一是团队里有两位主力开发用Eclipse写了十年Java转Kotlin学习成本高二是Java字节码在ProGuard混淆后体积比Kotlin小12%这对追求“APK小于8MB”的目标很关键三是Java的ThreadLocal和synchronized在IO密集型场景下比Kotlin协程更容易做线程安全审计——他们上线前做过压力测试同时打开50本书、每本缓存前100章Java版本内存峰值稳定在180MBKotlin协程版本因调度器开销波动到240MB以上。这不是教条主义是拿真机跑出来的数据说话。拒绝“智能”更是关键决策。项目里没有任何机器学习模块不搞“自动识别章节标题”“智能去广告”。所有规则必须人工编写、人工验证、人工标注。理由很朴素小说站点HTML结构千奇百怪但人类一眼就能看出“这个h3是标题那个p styletext-align:center是广告”。用算法去拟合准确率永远卡在92%~95%剩下的5%错误会导致整章内容错乱用户信任度归零。而人工规则写对一次永久有效。配套的web-editor.html就是为此服务的——它是个纯前端页面拖进去一个网页HTML输入XPath实时显示匹配结果再粘贴一段正文HTML写正则右边立刻高亮捕获内容。我试过用它调试“晋江文学城”的章节提取原来要花半小时试错现在三分钟搞定选中标题标签→右键“Copy XPath”→粘贴到编辑框→点“Test”标题列表秒出再选中正文段落→按CtrlShiftC抓取→生成正则div classnoveltext(?content[\s\S]*?)/div→测试通过。这种确定性才是生产力。3. 核心模块深度解析书源管理、缓存、夜间模式怎么炼成的3.1 书源管理JSON驱动的热插拔系统书源不是硬编码在Java类里的而是存在sources.json这个文件里。打开它你会看到类似这样的结构{ sources: [ { id: biquge_com, name: 笔趣阁, baseUrl: https://www.biquge.com.cn, bookListUrl: /book/{bookId}/, bookListXpath: //div[classl]/ul/li/a/href, bookNameXpath: //div[classinfo]/h1/text(), authorXpath: //div[classinfo]/p[1]/text(), chapterListXpath: //div[classlistmain]/dl/dd/a/href, chapterTitleXpath: //div[classlistmain]/dl/dd/a/text(), contentRegex: (?s)div id\content\(?content.*?)/div, encoding: gbk, timeout: 15000, enabled: true } ] }关键点在于bookListUrl里的{bookId}占位符——这不是模板字符串而是由BookSearchActivity.java里的generateBookUrl()方法动态替换的。当你在搜索框输入“斗破苍穹”它会先请求https://www.biquge.com.cn/search.php?q%E6%96%97%E7%A0%B4%E8%8B%8D%E7%A9%B9解析返回的搜索结果页提取a href/book/12345/中的12345再拼成https://www.biquge.com.cn/book/12345/。整个过程没有网络请求阻塞主线程因为用了AsyncTask注意不是ExecutorService因为AsyncTask在Android 11已被废弃但此项目最低只支持到Android 5.0所以用它反而更轻量。新增书源的操作路径是设置页→书源管理→右上角号→填表单→保存。表单提交后代码会做三件事1校验必填字段id不能重复、baseUrl必须含http、contentRegex必须含content组2用Jsoup.connect(testUrl).get()拉取测试页HTML3用你填的XPath/正则当场解析成功才写入JSON。这个“保存即验证”机制避免了大量无效书源堆积。我曾经手误把chapterListXpath写成//dl/dd/a/herf少个f点击保存时直接弹Toast“XPath解析失败未找到匹配元素”并高亮显示错误字段——这种即时反馈比编译报错有用十倍。3.2 缓存机制三级存储如何协同工作缓存不是简单地把HTML存SD卡而是精密的三级流水线一级内存缓存LruCache存的是ChapterContent对象含标题、正文、格式化时间戳大小设为Runtime.getRuntime().maxMemory() / 8约24MB在2GB内存手机上。关键优化是只缓存最近阅读的50章且每章内容超过50KB自动降级到二级缓存。这样既保证滑动流畅又防内存溢出。二级SQLite索引缓存ChapterCacheDBHelper表结构极简chapter_id(TEXT, PK),book_id(TEXT),title(TEXT),content_hash(TEXT),last_read_time(INTEGER)。注意content_hash不是存全文而是MD5(content.substring(0, 1000))——只取前1000字符哈希用于快速判断内容是否变更。真正内容存在三级缓存里这里只存指针。三级文件缓存FileCacheManager路径是/Android/data/com.xxx.mybookshelf/cache/chapters/文件名是bookId_chapterId.content内容是GZIP压缩后的UTF-8文本。为什么用GZIP实测对比未压缩50KB文本占磁盘52KBGZIP后仅18KB且Android解压耗时平均0.8msZInputStream远低于IO等待时间。删除策略是LRU空间阈值双控当缓存目录超500MB或最久未访问文件超30天触发清理。三者协作流程读取章节时先查内存→命中则返回未命中查SQLite→有记录则用content_hash比对本地文件→文件存在且哈希一致解压读取文件不存在或哈希不一致则网络请求→存入三级→更新二级索引→写入一级缓存。整个过程对UI线程零阻塞所有IO操作都在AsyncTask后台线程完成。我在MIUI 14上测试过连续翻100章内存占用曲线平滑无GC抖动。3.3 夜间模式不只是换个颜色夜间模式常被做成简单的Theme.AppCompat.DayNight切换但这套代码做了更深的定制。它不依赖系统主题而是自己维护两套CSS样式表白天模式CSSbody { background:#fff; color:#333; } p { line-height:1.8; margin:1em 0; }夜间模式CSSbody { background:#121212; color:#e0e0e0; } p { line-height:2.0; margin:1.2em 0; }关键在WebView加载时动态注入CSS。ReaderWebView.java里重写了onPageFinished()在页面加载完成后执行webView.evaluateJavascript( (function(){var styledocument.createElement(style); style.innerHTML cssContent ; document.head.appendChild(style);})(), null );这样做的好处是1字体抗锯齿更优系统主题切换有时导致WebView文字发虚2可单独控制段落间距、行高、首行缩进白天模式首行缩进2em夜间模式缩进3em缓解暗光下视觉疲劳3支持“护眼绿”模式——在夜间CSS基础上把color改成#98C379背景微调为#0A1929模拟墨水屏效果。这个功能藏在设置页底部“高级选项”里开关状态存在SharedPreferences的night_mode_type字段值为0(标准黑)/1(护眼绿)/2(深灰)。4. 实操全流程从导入工程到添加首个自定义书源4.1 环境准备与工程导入避坑指南第一步不是打开Android Studio而是检查JDK版本。项目gradle.properties里写着org.gradle.java.home/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/HomeMac路径对应Gradle插件版本com.android.tools.build:gradle:4.2.2。这意味着你必须用JDK 11用JDK 17会报Unsupported class file major version 61。我踩过的坑在Windows上装了JDK 17Android Studio自动选中它编译时报错一堆NonNull找不到——因为androidx.annotation:annotation:1.1.0在JDK 17下解析注解失败。解决方案File → Project Structure → SDK Location → JDK location手动指向JDK 11安装目录。第二步是签名文件处理。key.properties.jks是加密的密钥库但密码没写在代码里。打开app/build.gradle找到signingConfigs块signingConfigs { release { storeFile file(../key.properties.jks) storePassword System.getenv(KEYSTORE_PASSWORD) ?: changeit keyAlias System.getenv(KEY_ALIAS) ?: mykey keyPassword System.getenv(KEY_PASSWORD) ?: changeit } }看到没密码从环境变量读取。所以你要么在系统里设KEYSTORE_PASSWORDyourpass要么直接把?: changeit改成你的密码推荐后者省事。changeit是Java密钥库默认密码很多教程忽略这点导致签名失败。第三步是解决Gradle同步问题。build.gradle里有两处易错-compileSdkVersion 30→ 如果你用Android Studio Giraffe需升级到33但必须同步改targetSdkVersion否则运行时报SecurityException。-implementation androidx.appcompat:appcompat:1.2.0→ 这个版本太老和新SDK冲突。我改成1.6.1并在gradle.properties加一行android.useAndroidXtrue。导入后首次编译会卡在:app:mergeDebugResources因为res/values/strings.xml里有中文引号“”而旧版aapt2会把它当非法字符。解决方案用Notepad把整个strings.xml转成UTF-8无BOM格式再重试。4.2 添加自定义书源手把手实战我们以“新笔趣阁”https://www.xbiquge.la为例演示完整流程Step 1分析网页结构打开https://www.xbiquge.la/10_10837/《诡秘之主》主页按F12看Elements。发现- 小说名在div idinfoh1诡秘之主/h1/div- 章节列表在div idlistdldda href/10_10837/123456.html第一章/a/dd/dl/div- 章节内容在div idcontent正文文本.../divStep 2写XPath/正则用web-editor.html测试- 列表页XPath//div[idlist]//dd/a/href→ 测试返回/10_10837/123456.html- 章节标题XPath//div[idlist]//dd/a/text()→ 返回“第一章”- 内容正则(?s)div idcontent(?content.*?)/div→ 测试捕获成功Step 3构造JSON书源复制book_source_template.json填入{ id: xbiquge_la, name: 新笔趣阁, baseUrl: https://www.xbiquge.la, bookListUrl: /{bookId}/, bookListXpath: //div[idinfo]/h1/text(), chapterListXpath: //div[idlist]//dd/a/href, chapterTitleXpath: //div[idlist]//dd/a/text(), contentRegex: (?s)div id\content\(?content.*?)/div, encoding: utf-8, timeout: 12000 }注意bookListUrl是/{bookId}/因为主页URL就是https://www.xbiquge.la/10_10837/bookId直接取路径第一段。Step 4注入并验证把JSON粘贴到APP的“书源管理”→“导入JSON”里点确定。APP会自动解析并测试拉取https://www.xbiquge.la/10_10837/用bookListXpath提取书名若成功则保存。然后你就能在书架里搜到《诡秘之主》点开看第一章——内容完美呈现连段落空行都保留。5. 常见问题与排查技巧实录那些文档没写的真相5.1 典型问题速查表问题现象可能原因排查步骤解决方案搜索不到书提示“未找到结果”bookListUrl拼接错误或搜索页结构变更1用Chrome打开搜索URL看返回HTML是否有a href/book/12345/2检查BookSearchActivity.java里parseSearchResult()方法修改XPath或在preProcessScript里用JS修正HTML结构章节内容为空白contentRegex未捕获到content组或编码错误1在web-editor.html里粘贴章节HTML测试正则2查看Logcat过滤ParseError日志确保正则含(?content...)且encoding字段与网页meta charset...一致夜间模式失效WebView未注入CSS或CSS语法错误1在ReaderWebView.java的onPageFinished()里加log2检查CSS里是否有未闭合的{用在线CSS校验工具检查语法确保无import等不支持语法缓存不更新旧内容一直显示SQLite索引未刷新或文件缓存哈希未更新1用adb shell进入/data/data/com.xxx.mybookshelf/files/删sources.json2查chapter_cache.db里对应chapter_id的content_hash在FileCacheManager.java的saveChapter()里确认updateHashInDB()被调用5.2 独家避坑技巧技巧1XPath调试的“三明治法”别一上来就写复杂XPath。先用最粗粒度//*看Jsoup返回多少节点再加一层//div看数量是否合理最后精确到//div[idcontent]。我调试“起点中文网”时发现它用div idreadfree包裹正文但XPath写成//div[idcontent]永远为空——因为真实ID是readfree。用“三明治法”层层缩小范围比盲目猜高效十倍。技巧2正则的“最小贪婪”原则(?s)div(?content.*?)/div里的?不是可有可无。没有它.*会贪婪匹配到最后一个/div导致整页HTML被吞。加?变成惰性匹配只到第一个/div就停。我在处理“晋江文学城”时原文有多个div嵌套没加?导致捕获内容包含大量无关HTML标签。技巧3书源启用/禁用的隐藏逻辑sources.json里enabled: false的书源不是简单跳过而是参与搜索但不显示结果。这是为了防止误操作禁用后用户以为功能坏了。如果你想彻底移除得删掉整个JSON对象或改id为disabled_xbiquge_la加前缀让它不被识别。技巧4ProGuard混淆的致命陷阱proguard-rules.pro里有一行-keep class com.mybookshelf.reader.parser.** { *; }但漏了HtmlParser的内部类。某次我升级Jsoup到1.15.3它新增了Elements内部类结果混淆后parseList()返回空集合。解决方案在ProGuard里加-keep class org.jsoup.select.** { *; }把Jsoup所有类都保留。最后分享个小技巧想快速验证某个书源是否还活着不用打开APP直接用命令行curl -s https://www.biquge.com.cn/book/12345/ \| grep -o title.*/title如果返回title斗破苍穹最新章节_斗破苍穹无弹窗广告_斗破苍穹全文阅读/title说明站点正常XPath大概率还能用。这比每次编译APP快多了。本文还有配套的精品资源点击获取简介一套开箱即用的Android小说阅读APP源码纯Java开发基于Android Studio构建支持直接编译安装运行。内置超300个已验证可用的小说书源覆盖主流免费小说站点所有书源均按标准JSON格式组织便于查看与复用。提供完整的书源管理界面用户可手动添加新书源、编辑现有规则如列表页XPath、内容页正则、章节提取逻辑等无需重新编译即可生效。源码模块划分清晰包含书源解析引擎、缓存机制、离线下载、夜间模式、字体/排版调节、书架同步等实用功能。工程结构符合Android官方推荐规范含gradle配置、签名文件key.properties.jks、混淆规则proguard-rules.pro、README文档及常见问题说明。配套提供书源整理工具和web-editor.html在线规则调试页面方便快速验证XPath与正则表达式。适配Android 5.0至Android 14无第三方SDK依赖导入即调适合Java安卓开发者学习源码逻辑、二次定制或搭建私有阅读平台。本文还有配套的精品资源点击获取