- 新增浏览器模块技术文档,涵盖 BrowserCore、BrowserWindow 等核心组件 - 添加事件系统文档,包括 EventBus、GlobalEventBus 及各类事件定义 - 创建 LanguageManager 国际化管理器详细说明文档 - 新增 Log4j2OutputStream 标准输出重定向类文档 - 添加 Main 入口类启动流程与路由机制说明 - 创建 BrowserCreationCallback 回调接口使用指南 - 完善 AxisInnovatorsBox 主类架构与崩溃诊断系统文档
137 lines
5.9 KiB
Markdown
137 lines
5.9 KiB
Markdown
这份文档是对 `com.axis.innovators.box.browser` 包核心架构的深度解析。它涵盖了从底层核心代理到高层 UI 容器,以及 Java-JS 桥接机制的所有关键类。
|
||
|
||
---
|
||
|
||
# Axis Innovators Box Browser 核心类技术文档
|
||
|
||
本模块旨在为 Swing 应用程序提供一个高性能、易扩展的嵌入式浏览器解决方案。通过高度抽象的架构,开发者可以轻松地在 Java 窗口中嵌入 Web 页面,并实现双向通信。
|
||
|
||
---
|
||
|
||
## 1. 核心架构设计
|
||
|
||
系统采用了 **代理模式 (Proxy Pattern)** 和 **构建者模式 (Builder Pattern)**:
|
||
- **核心逻辑** (`BrowserCore`) 与 **窗口容器** (`BrowserWindow`/`BrowserWindowJDialog`) 分离。
|
||
- **配置逻辑** (`BaseBrowserBuilder`) 统一管理窗口属性。
|
||
- **通信逻辑** (`JsBridgeController`) 通过注解驱动。
|
||
|
||
---
|
||
|
||
## 2. 接口与基类 (抽象层)
|
||
|
||
### 2.1 `BrowserContainer.java` (统一接口)
|
||
这是所有浏览器窗口容器的通用协议。无论是 `JFrame` 还是 `JDialog`,都必须实现此接口。
|
||
- **核心职责**:提供对外的统一 API,使得 `WindowRegistry` 和业务逻辑可以忽略窗口的具体物理类型。
|
||
- **关键方法**:
|
||
- `executingJsCode(String script)`: 在 JS 环境中执行代码(支持跨线程)。
|
||
- `setController(JsBridgeController controller)`: 动态切换 Java 逻辑控制器。
|
||
- `getBrowser()` / `getMsgRouter()`: 获取底层 CEF 对象。
|
||
|
||
### 2.2 `BaseBrowserBuilder.java` (通用配置器)
|
||
采用泛型链式调用的抽象构建器类。
|
||
- **核心职责**:存储所有窗口初始化所需的配置数据(尺寸、标题、初始 URL、图标等)。
|
||
- **主要字段**:
|
||
- `htmlUrl` / `htmlPath`: 指定要加载的内容来源。
|
||
- `operationHandler`: 系统级消息处理器。
|
||
- `controller`: 初始的 JSBridge 控制器。
|
||
- `size`, `title`, `resizable`: 基础窗口属性。
|
||
|
||
---
|
||
|
||
## 3. UI 容器层 (实现层)
|
||
|
||
### 3.1 `BrowserWindow.java`
|
||
继承自 `JFrame`,用于创建独立的主窗口或顶层窗口。
|
||
- **特点**:支持任务栏图标、最大化/最小化等标准窗口行为。
|
||
- **内部实现**:通过 `BrowserCore` 初始化浏览器组件,并将其作为 `BorderLayout.CENTER` 添加到内容面板。
|
||
|
||
### 3.2 `BrowserWindowJDialog.java`
|
||
继承自 `JDialog`,用于创建子窗口或模态/非模态对话框。
|
||
- **特点**:支持设置 `parentFrame`,可以实现点击父窗口时弹窗保持在最前的逻辑。
|
||
- **应用场景**:设置界面、详情弹窗、登录界面等。
|
||
|
||
---
|
||
|
||
## 4. 浏览器引擎核心
|
||
|
||
### 4.1 `BrowserCore.java` (系统大脑)
|
||
这是最关键的类,封装了 JCEF 的所有复杂逻辑。
|
||
- **职责**:
|
||
1. **初始化**:管理 `CefClient` 的创建和 `CefBrowser` 的生命周期。
|
||
2. **Handler 路由**:自动设置加载监听、右键菜单处理、证书错误处理、文件选择对话框处理等。
|
||
3. **通信初始化**:配置 `CefMessageRouter` (基于 `window.javaQuery`)。
|
||
4. **脚本队列**:维护 `pendingScripts` 队列。如果页面尚未加载完成就调用了 JS 代码,代码会暂存在队列中,直到 `onLoadEnd` 触发后自动冲刷执行。
|
||
5. **资源注入**:自动向 Web 页面注入系统字体、主题颜色和 `extLibsPath` 等全局变量。
|
||
|
||
---
|
||
|
||
## 5. Java-JS 桥接机制 (通信层)
|
||
|
||
这是实现“Java 调用 JS”和“JS 调用 Java”的核心组件。
|
||
|
||
### 5.1 `JsMapping.java` (方法注解)
|
||
一个运行时注解,用于标记控制器中可以被 JS 访问的方法。
|
||
- **属性 `value()`**:
|
||
- 若为空:默认挂载在 `tzd.方法名()`。
|
||
- 若为单纯字符串(如 `"login"`):挂载在 `tzd.login()`。
|
||
- 若包含点号(如 `"app.utils.calc"`):挂载在 `window.app.utils.calc()`。
|
||
|
||
### 5.2 `JsBridgeController.java` (抽象控制器)
|
||
用户自定义业务逻辑的基类。
|
||
- **工作原理**:
|
||
1. **反射扫描**:在初始化时自动扫描带有 `@JsMapping` 的所有方法。
|
||
2. **代码生成**:`generateInjectionJs()` 会生成一套复杂的 JS 代理对象代码,在 `onLoadEnd` 时注入浏览器。
|
||
3. **参数转换**:利用 Gson 将 JS 传来的 JSON 数组自动反序列化为 Java 方法的参数类型(如 `String`, `int`, `boolean` 或自定义 POJO)。
|
||
4. **异步回调**:封装了 `window.javaQuery` 的复杂调用,使 JS 端可以像调用本地函数一样使用 `async/await` 获取结果。
|
||
|
||
---
|
||
|
||
## 6. 开发流程示例
|
||
|
||
### 第一步:定义控制器
|
||
```java
|
||
public class MyActionController extends JsBridgeController {
|
||
@JsMapping("saveConfig") // JS 调用: tzd.saveConfig(data)
|
||
public boolean save(String data) {
|
||
System.out.println("Saving: " + data);
|
||
return true;
|
||
}
|
||
|
||
@JsMapping("sys.getInfo") // JS 调用: sys.getInfo()
|
||
public String getInfo() {
|
||
return "Version 1.0.0";
|
||
}
|
||
}
|
||
```
|
||
|
||
### 第二步:启动窗口
|
||
```java
|
||
WindowRegistry.getInstance().createNewChildWindow("win01", builder -> {
|
||
builder.title("我的应用")
|
||
.htmlPath("index.html")
|
||
.controller(new MyActionController()); // 绑定
|
||
});
|
||
```
|
||
|
||
### 第三步:前端调用
|
||
```javascript
|
||
// index.html
|
||
async function doSave() {
|
||
// 调用 Java 并获取返回值
|
||
const success = await tzd.saveConfig("{id: 1}");
|
||
if (success) {
|
||
const info = await sys.getInfo();
|
||
alert("保存成功, " + info);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 最佳实践建议
|
||
|
||
1. **资源管理**:始终通过 `WindowRegistry.unregisterWindow()` 关闭窗口,以确保内存和进程资源被释放。
|
||
2. **线程注意事项**:
|
||
- `executingJsCode` 是线程安全的,可以在任意线程调用。
|
||
- 在 `@JsMapping` 标记的 Java 方法中,如果涉及 UI 更新(如修改 Swing 组件),必须包裹在 `SwingUtilities.invokeLater` 中。
|
||
3. **动态切换**:由于 `setController` 会重新注入脚本,建议在页面初次加载完成后进行控制器切换,以获得最佳稳定性。 |