🧩 SilverGhost
silverghost 核心 门面 + SPI
面向 GUI 的核心 API 类,编排归档读取、ProGuard 映射、命名元素翻译三阶段流水线。
📁 源码:
ClassySharkWS/src/com/google/classyshark/silverghost/SilverGhost.java 📦 包:com.google.classyshark.silverghost职责
SilverGhost 是 silverghost 子系统的核心 API 类,面向图形界面场景。它在类注释中明确警告「永远不要从 UI 线程调用 readXXX 方法」,因为其内部会执行解压、翻译等耗时操作。它编排三阶段流程:1) ContentReader 读取归档内容并实例化 Reducer;2) 读取 ProGuard 映射文件构建 TokensMapper;3) 通过 TranslatorFactory 翻译用户选中的命名元素。同时它会在 readContents 阶段预提取 APK 的 AndroidManifest.xml 文本,供后续 getManifestMatches 做快速子串搜索。
关键方法 / 字段
| 名称 | 类型 | 说明 |
|---|---|---|
tokensMapper | static TokensMapper | 符号重映射 SPI 插槽,静态块默认为 IdentityMapper |
fullArchiveReader | static FullArchiveReader | 全量归档读取 SPI 插槽,静态块默认为 EmptyFullArchiveReader |
readContents() | void | 阶段 1:建 ContentReader、实例化 Reducer、提取 Manifest、触发 fullArchiveReader 异步读取 |
readMappingFile(File) | TokensMapper | 阶段 2:让 tokensMapper 读取 ProGuard 映射并返回自身 |
addMappings(TokensMapper) | void | 阶段 2 替代入口:直接注入一个已构建的映射器 |
translateArchiveElement(String) | void | 阶段 3:用 TranslatorFactory 创建翻译器、注入 mapper、调用 apply |
getManifestMatches(String) | List<Translator.ELEMENT> | 在预提取的 manifestStr 中子串搜索,带上下文 2 行,用 :::: 分隔 |
isArchiveError() | boolean | 检测「空归档」或「仅含 AndroidManifest.xml 的清单 APK」错误情形 |
filter(String) | List<String> | 委托 Reducer 做类名前缀过滤 |
getComponents() | List<ContentReader.Component> | 返回归档内组件列表(dex/jar/native 等) |
工作流程
mermaid
flowchart TD
A["setBinaryArchive(file)"] --> B["阶段1: readContents()"]
B --> C["ContentReader.load() 读归档"]
C --> D["new Reducer(allClassNames)"]
D --> E{"后缀 .apk?"}
E -- 是 --> F["TranslatorFactory 翻译 AndroidManifest.xml"]
F --> G["manifestStr = translator.toString()"]
E -- 否 --> H["fullArchiveReader.readAsyncArchive()"]
G --> H
H --> I["阶段2: readMappingFile / addMappings"]
I --> J["阶段3: translateArchiveElement(name)"]
J --> K["TranslatorFactory.createTranslator(..., fullArchiveReader)"]
K --> L["translator.addMapper(tokensMapper)"]
L --> M["translator.apply() 翻译"]设计要点
- 🔍 三阶段编排显式编号 — 源码中以注释
1. READ CONTENTS/2. READ MAPPINGS FILE/3. BINARY ARCHIVE ELEMENT标注阶段,流程清晰、阶段解耦。 - 🧩 两个 SPI 插槽 —
tokensMapper(默认IdentityMapper)与fullArchiveReader(默认EmptyFullArchiveReader)均在静态块注入默认实现,是可替换的扩展点;setBinaryArchive还会重置tokensMapper回 IdentityMapper 以防残留状态。 - 🎨 Manifest 预提取 + 快速搜索 —
readContents提前把 AndroidManifest 翻成字符串缓存到manifestStr,getManifestMatches再做内存子串搜索,避免每次查询都重新解压翻译。 - 📊 上下文 + 分隔符标记 —
getManifestMatches给命中行前后各补 2 行上下文,并以::::长分隔线作为Translator.TAG.SELECTION标记,配合Translator.ELEMENT体系供 GUI 高亮渲染。 - ⚠️ 线程约束写在注释 — 类 Javadoc 直接声明「never call readXXX method from UI thread」,依赖调用方自行保证后台线程执行。
- 🧩 fullArchiveReader 回退链 —
translateArchiveElement把fullArchiveReader透传给TranslatorFactory,使无匹配扩展名的元素可走全量归档读取回退路径。
协作关系
- 依赖:[[ContentReader]]
- 依赖:[[Reducer]]
- 依赖:[[TranslatorFactory]]
- 依赖:[[Translator]]
- 依赖:[[TokensMapper]]
- 依赖:[[FullArchiveReader]]
- 依赖:[[IdentityMapper]]
- 依赖:[[EmptyFullArchiveReader]]
- 被调用:[[Shark]](间接经 SilverGhostFacade)
已知问题 / TODO
- ⚠️
setBinaryArchive内注释TODO think of initialyzing data members as they hold prev file state,成员字段可能残留上一个文件状态,目前仅手动重置tokensMapper。 - ⚠️
translateArchiveElement内注释TODO handle case when reducer is null or whatever,缺少对 reducer 为空等异常场景的防御。 isArchiveError判定「仅含 AndroidManifest.xml」依赖类名集合恰好 size==1 且包含该字符串,边界较脆弱。