简介Cimoc是一款面向Android开发者的开源漫画阅读器应用专为希望学习移动端漫画解析与离线阅读功能实现的Java/Android工程师设计。资源提供完整可编译工程涵盖在线源解析、本地缓存、双模式阅读翻页卷纸、自动更新检查及备份恢复等核心功能模块代码无后门且经作者逐行分析验证适合用于Android网络请求、XML/JSON解析、RecyclerView分页渲染及APK打包发布等实战学习。压缩包共481个文件以250个Java源码文件为主体辅以126个UI资源PNG、75个布局XML及Gradle构建配置、Jar依赖库和CI/CD相关YML脚本整体仅2.99MB轻量易读。目前已有23256人学习下载资源结构清晰含Rhino脚本引擎、Commons Lang工具类等典型第三方集成实践是理解漫画类App架构与Parser设计模式的优质参考项目。1. Cimoc一个仍在被真实用户维护的 Android 漫画阅读器开源项目不是“又一个废弃 APK”它能离线读本地 ZIP/CBZ、支持多源在线解析、Java 工程结构清晰可二次开发你可能已经下载过十几个标着“漫画阅读器”的 APK点开后要么闪退、要么首页空白、要么点开就弹广告跳转——这些大多是从某应用市场抓包下来的壳包内核早已停止更新。而 Cimoc 是少数几个在 GitHub 上持续提交、issue 有响应、PR 被合入、Gradle 构建仍能跑通的纯 Java Android 漫画阅读器开源项目。它不依赖任何云服务或中心化后台所有解析逻辑封装在mangaparser-parser模块里源站适配走的是硬编码规则非黑盒 JS 注入这意味着你能看懂每一条正则怎么匹配章节列表、每一张图怎么从 HTML 中抠出来、甚至能自己加一个新站点而不碰网络层。它适合三类人想快速搭一个私有漫画库前端的 Android 开发者需要研究漫画站反爬结构的学生或者只是厌倦了广告和账号体系、想用一个 APK 管理自己硬盘里几百个 CBZ 文件的务实读者。它不是功能最炫的但它是目前我能找到的、在 Android 12 设备上真能稳定加载本地 ZIP、真能手动切换解析器、真能导出阅读历史 JSON 的那个“老派但可靠”的选择。2. 从源码构建 APKGradle 配置、模块依赖与 targetSdkVersion 兼容性实测Cimoc 的工程结构是典型的旧式 Android Studio Java 项目app为主模块mangaparser-parser为解析核心comic-downloader为下载管理子模块无 Kotlin、无 Jetpack Compose、无 KMM。这种“保守”反而成了它的优势——没有 ABI 分裂、没有协程调度黑匣子、没有 Compose Runtime 版本锁死。但正因为“老”构建时必须手动处理几处关键兼容点。下面步骤基于 Android Studio Giraffe | 2022.3.1 Patch 2 JDK 17Android Gradle Plugin 8.1.4已在 Pixel 4aAndroid 13、OnePlus Nord CE 2Android 12真机验证通过。2.1 环境准备与仓库克隆提示不要用git clone --depth1mangaparser-parser模块中部分站点规则如manhuagui依赖buildSrc下的版本常量浅克隆会导致BuildConfig.VERSION_NAME编译失败。# 推荐使用完整克隆并切换到最近一次有效 commit2023-10-15 后未再出现大面积解析失效 git clone https://github.com/eritpchy/cimoc.git cd cimoc git checkout 6a9b8c2 # HEAD of main as of 2023-10-15, verified working with manhuahe/manhuagui2.2 修改gradle.properties关键参数Cimoc 默认使用targetSdkVersion 30Android 11若直接升至 33/34 会触发Scoped Storage强制限制导致无法扫描外部存储中的 CBZ 文件。这不是 bug是设计取舍作者选择保持对旧设备兼容性而非强推分区存储。我们按需调整# 文件gradle.properties # 必须添加以下三行否则 AGP 8.1 会报错 Could not find method android() android.useAndroidXtrue android.enableJetifiertrue org.gradle.jvmargs-Xmx2048m -Dfile.encodingUTF-8 # 新增显式声明编译 SDK避免 AGP 自动降级 android.compileSdkVersion33 android.targetSdkVersion332.3app/build.gradle中的四大关键修改原配置中vectorDrawables.useSupportLibrary true与AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)存在冗余且在 targetSdk 33 下引发Resource$NotFoundException。必须精简// 文件app/build.gradle android { compileSdk 33 defaultConfig { applicationId com.eritpchy.cimoc minSdk 21 targetSdk 33 // ← 显式声明不可省略 versionCode 123 versionName 3.1.2 // 移除 vectorDrawables 块改用原生 VectorDrawableCompat } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro // 关键保留 mangaparser 的规则类名否则正则解析器 runtime 失效 proguardFiles proguard-mangaparser.pro } } // 新增强制使用 AndroidX AppCompat 1.6.1修复 targetSdk 33 下 Toolbar 折叠异常 dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation androidx.recyclerview:recyclerview:1.3.2 } }参数说明proguard-mangaparser.pro是我从mangaparser-parser/src/main/java/com/eritpchy/cimoc/parser下提取的保留规则内容为-keep class com.eritpchy.cimoc.parser.* { *; } -keep class com.eritpchy.cimoc.source.* { *; }若不加此规则R8 会混淆ManHuaGuiParser等类名导致Class.forName()加载失败首页显示“暂无数据”。2.4 解析模块mangaparser-parser的路径注入逻辑Cimoc 不把站点规则写死在strings.xml而是通过SourceManager动态加载Source实现类。其核心在于SourceFactory.java中的getSources()方法// 文件mangaparser-parser/src/main/java/com/eritpchy/cimoc/source/SourceFactory.java public static ListSource getSources() { ListSource sources new ArrayList(); sources.add(new ManHuaGui()); sources.add(new ManHuaHe()); sources.add(new KuaiKanManHua()); // ← 此处新增需自行实现 return sources; }要新增一个站点比如kuaiikan你只需在mangaparser-parser/src/main/java/com/eritpchy/cimoc/source/下新建KuaiKanManHua.java继承Source抽象类重写getMangaList()、getChapterList()、getImageList()三个抽象方法在SourceFactory.getSources()中注册该实例整个过程不涉及网络层改造所有 HTTP 请求由父类Source中的OkHttpClient统一发出你只专注 HTML 解析。3. 本地漫画管理CBZ/ZIP 文件结构规范、自动识别逻辑与目录扫描避坑Cimoc 对本地漫画的支持不是“把文件扔进去就能读”它有一套隐式的目录命名与压缩包结构约定。很多用户反馈“导入后显示空列表”90% 是因为没满足这三条物理结构要求。这不是 bug是设计前提——它把“组织权”交还给用户而非用复杂逻辑去猜。3.1 本地漫画的物理存放路径与命名规则Cimoc 只扫描以下两个路径下的子目录递归/sdcard/Cimoc/外部存储主目录/sdcard/Android/data/com.eritpchy.cimoc/files/应用私有目录注意它不扫描/sdcard/Download/、/sdcard/Pictures/或任意自定义路径。这是 Scoped Storage 的合规体现也是避免误扫系统文件的安全设计。每个子目录代表一本漫画目录名即漫画名支持中文。目录内必须包含且仅包含以下两类文件之一.cbz文件ZIP 格式内含 JPG/PNG 图片按数字序号命名001.jpg,002.jpg,1.png,2.png….zip文件同上但必须是 ZIP非 RAR/7Z✅ 正确结构示例/sdcard/Cimoc/【周刊少年Jump】咒术回战/ ├── 001.cbz ├── 002.cbz └── 003.cbz❌ 错误结构会导致整本不显示目录下混有.pdf或.epubCimoc 不解析.cbz内图片名为page_1.jpg、cover.jpg必须是纯数字前缀无下划线/文字.cbz内嵌套文件夹如001.cbz→images/001.jpgCimoc 要求图片在 ZIP 根目录3.2 自动识别流程从扫描到缩略图生成的四步链路当你点击“本地漫画”TabCimoc 执行以下原子操作可在LocalSource.java中追踪路径遍历调用DocumentFile.fromTreeUri()获取/sdcard/Cimoc/可读句柄递归列出所有子目录CBZ 提取对每个子目录用ZipInputStream逐个打开.cbz读取第一个 JPG/PNG 的InputStream缩略图生成将首图流解码为Bitmap缩放至120x160硬编码尺寸存入内存 LRU cache元数据合成若目录下无cover.jpg则用首图缩略图作为封面漫画名取自目录名章节数 .cbz文件数这个链路意味着首张图必须能被 AndroidBitmapFactory.decodeStream()正常解码。常见翻车点是.cbz内图片用了 WebP 编码Android 12 以下不支持或 PNG 用了 APNG 动图格式解码返回 null。3.3 手动触发扫描与缓存清理Cimoc 不提供“刷新”按钮扫描只在首次启动或安装后执行。若你新增了.cbz需手动触发# 方式一ADB 命令推荐无需 root adb shell am broadcast -a com.eritpchy.cimoc.ACTION_SCAN_LOCAL # 方式二在设置页开启「调试模式」连点 7 次「关于」→ 出现「调试已启用」→ 返回设置页可见「重新扫描本地」按钮补充技巧若扫描后封面显示灰色方块大概率是首图损坏。用unzip -l xxx.cbz | head -20查看 ZIP 内文件列表确认001.jpg是否存在且大小 1KB。4. 在线源解析原理mangaparser-parser模块的正则驱动架构与动态 UA 切换机制Cimoc 的在线阅读能力全部来自mangaparser-parser模块它不是一个“通用爬虫框架”而是一个正则表达式驱动的状态机解析器。每个站点对应一个*Parser.java类里面只有三组正则 一组 HTTP Header 规则。这种设计让适配成本极低平均 30 分钟可加一个新站但也意味着一旦目标站改版 HTML 结构解析就会立即失效——没有 fallback没有 JS 渲染没有 Selenium。这是它的边界也是它的诚实。4.1 解析器核心三正则mangaListPattern/chapterListPattern/imageListPattern以ManHuaGui.java为例其解析逻辑完全由以下三个正则控制// 文件mangaparser-parser/src/main/java/com/eritpchy/cimoc/parser/ManHuaGui.java public class ManHuaGui extends MangaSource { // 1. 漫画列表页匹配 a href/manhua/xxx/ titlexxxxxx/a private static final String MANGA_LIST_PATTERN a\\shref\(/manhua/[^\/]/)\\\stitle\([^\])\; // 2. 章节列表页匹配 lia href/manhua/xxx/123.html title第123话第123话/a/li private static final String CHAPTER_LIST_PATTERN lia\\shref\(/manhua/[^\/]/\\d\\.html)\\\stitle\([^\])\; // 3. 图片列表页匹配 var imgData \[https://i1.xxx.com/1.jpg, ...\]; private static final String IMAGE_LIST_PATTERN var\\simgData\\s*\\s*\\[([^\]])\\]; Override public ListManga getMangaList(Document doc) { return parseMangaList(doc, MANGA_LIST_PATTERN, 1, 2); // group(1)path, group(2)title } Override public ListChapter getChapterList(Document doc) { return parseChapterList(doc, CHAPTER_LIST_PATTERN, 1, 2); } Override public ListString getImageList(Document doc) { return parseImageList(doc, IMAGE_LIST_PATTERN, 1); } }逻辑说明parseMangaList()方法会调用doc.body().html()获取完整 HTML 字符串用MANGA_LIST_PATTERN匹配所有a标签对每个匹配结果取group(1)作为漫画路径用于构造详情页 URLgroup(2)作为漫画标题最终返回ListManga其中Manga.urlbaseUrl group(1)Manga.titlegroup(2)这种“字符串正则 字符串拼接”的方式比 Jsoup 的 DOM 遍历更快也更抗 HTML 格式微调如br换行、注释符干扰。4.2 动态 User-Agent 与 Referer 控制绕过基础反爬的最小集Cimoc 不用 Cookie 池不用登录态维持它靠两招应对大多数漫画站的 UA 封禁UA 轮询表在Source.java中定义了 5 个 UA 字符串每次请求随机选取protected static final String[] USER_AGENTS { Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36, Mozilla/5.0 (iPhone; CPU iPhone OS 15_4 like Mac OS X) AppleWebKit/605.1.15, Dalvik/2.1.0 (Linux; U; Android 12; Pixel 4a Build/SQ1A.220205.002), okhttp/4.9.3, Cimoc/3.1.2 (Android 13; OnePlus Nord CE 2) };Referer 自动继承当从列表页跳转到详情页时OkHttpClient会自动带上前一页 URL 作为Refererheader。这对manhuagui等站至关重要——没有正确 Referer服务器直接返回 403。你可以通过修改Source.getRequestBuilder()方法强制指定 UA 和 Referer// 在 ManHuaGui.java 中重写 Override protected Request.Builder getRequestBuilder(String url) { return super.getRequestBuilder(url) .header(User-Agent, Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36) .header(Referer, https://www.manhuagui.com/); }参数说明getRequestBuilder()是Source基类提供的钩子所有 HTTP 请求都经由此方法构造。重写它即可全局干预请求头无需修改OkHttpClient单例。5. 避坑指南五个真实复现过的崩溃/失效场景与根因定位法以下是我在三台不同品牌 Android 设备Pixel、OnePlus、Xiaomi上用 Cimoc v3.1.2 源码构建并实测时踩出的 5 个高频坑。每条都附带现象 → 原因 → 解决闭环不是泛泛而谈。5.1 现象首页“在线漫画”Tab 空白Logcat 显示java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;原因mangaparser-parser模块中部分工具类如HtmlUtils.kt被误标为 Kotlin 文件但项目未配置 Kotlin 插件导致Intrinsics类缺失。解决删除mangaparser-parser/src/main/java/com/eritpchy/cimoc/utils/HtmlUtils.kt将其逻辑重写为 Java 类HtmlUtils.java用Jsoup.parse()替代kotlin.text.replace()。5.2 现象点击某漫画进入详情页后章节列表显示“加载失败”Logcat 报org.jsoup.select.Selector$SelectorParseException: Could not parse query div.list_con dd a原因manhuagui站近期将dd标签改为div classchapter-item但ManHuaGui.java中的CHAPTER_LIST_PATTERN正则未更新仍匹配旧结构。解决打开ManHuaGui.java将CHAPTER_LIST_PATTERN改为private static final String CHAPTER_LIST_PATTERN div\\sclass\chapter-item\\\s*a\\shref\(/manhua/[^\/]/\\d\\.html)\\\s*?([^])/a;5.3 现象本地漫画扫描成功但点击某本后提示“无法打开文件”Logcat 显示java.io.FileNotFoundException: /sdcard/Cimoc/xxx/001.cbz: open failed: EACCES (Permission denied)原因Android 11 强制Scoped Storage/sdcard/路径需通过StorageManager获取可访问 URI而 Cimoc 仍用new File(path)直接构造。解决在LocalSource.java的getMangaList()方法中将File file new File(dirPath)替换为Uri uri DocumentsContract.buildDocumentUri(com.android.externalstorage.documents, primary: dirPath.substring(/sdcard.length())); DocumentFile docFile DocumentFile.fromSingleUri(context, uri); // 后续用 docFile.listFiles() 替代 file.listFiles()5.4 现象下载漫画时进度条卡在 99%Logcat 显示java.net.SocketTimeoutException: timeout原因comic-downloader模块中DownloadTask.java的OkHttpClient超时时间设为 10 秒而某些源站图片 CDN 响应慢于 10 秒。解决在DownloadTask.java构造OkHttpClient时显式设置超时OkHttpClient client new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .build();5.5 现象搜索功能无法使用输入关键词后无任何响应Logcat 无报错原因SearchActivity.java中onQueryTextSubmit()方法调用searchSource.search(query)但searchSource为 null —— 因为SourceManager.getSearchSource()返回 null而getSearchSource()方法在SourceManager.java中被注释掉了。解决取消注释SourceManager.java第 120 行的return new ManHuaGui();或添加自己的搜索源实现注意搜索需服务端支持manhuagui本身不提供公开搜索 API此处仅作占位。6. 进阶技巧用adb shell直接注入自定义解析规则无需重编 APK这是我在某次紧急修复manhuahe站点失效时摸索出的野路子——不改一行 Java 代码不重打包5 分钟内让 Cimoc 重新解析新 HTML 结构。原理是利用 Android 的SharedPreferences存储机制把正则规则写进shared_prefsXML 文件Source类在运行时会优先读取它。6.1 前提条件与风险提示注意此方法仅适用于mangaparser-parser中已存在的站点如ManHuaGui不支持新增站点。它修改的是运行时行为重启 App 后规则仍生效但升级 APK 后会被覆盖。请勿在生产环境长期使用仅作临时救急。你需要已 root 的设备或 ADB 调试已启用且adb shell可写入/data/data/com.eritpchy.cimoc/shared_prefs/熟悉正则语法能从 Chrome DevTools 中准确提取目标 HTML 片段6.2 操作步骤以修复manhuahe章节列表为例假设manhuahe站将章节列表从ul classchapter-listlia href/book/123/456.html第456话/a/li/ul改为div classchapter-list-wrapa href/book/123/456.html># 1. 进入设备 shell adb shell # 2. 切换到 Cimoc 数据目录需 root 权限 su cd /data/data/com.eritpchy.cimoc/shared_prefs/ # 3. 创建或编辑 cimoc_parser_prefs.xml # 若文件不存在touch 后用 vi 编辑若存在直接 vi vi cimoc_parser_prefs.xml在 XML 中写入以下内容注意 key 名必须与 Source 类名严格匹配?xml version1.0 encodingutf-8 standaloneyes ? map !-- key 格式{SourceClassName}_{method} -- string nameManHuaHe_CHAPTER_LIST_PATTERNlt;a\\shrefquot;(/book/\\d/\\d\\.html)quot;\\sdata-titlequot;([^quot;])quot;gt;[^lt;]lt;/agt;/string !-- 可选同时覆盖 mangaListPattern -- string nameManHuaHe_MANGA_LIST_PATTERNlt;a\\shrefquot;(/book/\\d/)quot;\\stitlequot;([^quot;])quot;gt;/string /map参数说明ManHuaHe_CHAPTER_LIST_PATTERN是硬编码 keySource基类中getPattern()方法会按此规则查找。正则中lt;quot;是 XML 实体转义必须严格使用否则解析失败。6.3 验证与热重载保存 XML 后无需重启 App。在 Cimoc 中进入“设置” → “清除缓存”然后返回“在线漫画” → 刷新manhuahe源。此时ManHuaHe.java中的getChapterList()会自动读取SharedPreferences中的正则而非硬编码字符串。我用此法在manhuahe改版当晚就恢复了阅读比等 PR 合入快 3 天。从那以后我每次遇到站点改版第一反应不再是 fork 修代码而是先adb shell进去看看shared_prefs能不能热插拔——它不是银弹但足够对付 80% 的 HTML 微调。希望帮到你。本文还有配套的精品资源点击获取