# webview-container — Android Java SDK Library

腾讯电子签 H5 容器 SDK Android **纯 Java** 实现的核心 Library 模块，与 `android-kotlin/webview-container` API 对等，适用于 100% Java 代码库的存量客户（避免引入 Kotlin 运行时）。

## 模块标识

| 项 | 值 |
|---|---|
| Gradle 路径 | `:webview-container` |
| Java package | `com.tencent.qian.webview` |
| 产物类型 | Android Library（AAR） |
| 语言 | Java 17 |
| 集成方式 | Gradle `implementation project(':webview-container')` 或拷贝模块 |

## 目录结构

```
webview-container/
├── src/
│   ├── main/
│   │   ├── java/com/tencent/qian/webview/         # 源码（与 Kotlin 版结构对齐）
│   │   ├── res/                                   # 顶栏图标、字符串、主题
│   │   └── AndroidManifest.xml
│   └── test/                                      # 单元测试（JUnit 4 + Robolectric）
├── build.gradle                                   # 模块级 Groovy DSL
├── consumer-rules.pro                             # 消费方 ProGuard 规则
└── proguard-rules.pro                             # 自身 ProGuard 规则
```

## 公共 API 速览（Builder 模式）

### 容器模式

SDK 内置承载页（含顶栏、进度条、关闭确认、Edge-to-Edge 适配），一次调用即可拉起签署 H5：

```java
TencentQianWebView.open(
    activity,
    "https://quick.qian.tencent.cn/sign/...?flowId=...",
    new TencentQianWebViewConfig.Builder()
        .topBarTitle("电子签署")
        .confirmBeforeClose(true)
        .build(),
    new SignCallback() {
        @Override
        public void onSignResult(SignResult result) {
            // result.getAction() / result.getResult() / result.getFlowId() / result.getExtras()
        }
    }
);
```

### 组件模式 `TencentQianWebViewComponent`

将签署 WebView 嵌入宿主自定义布局，顶栏 / 进度条 / 错误页由宿主自绘。参数面与 Kotlin DSL 一一同名，仅形态为 Builder：

```java
TencentQianWebViewController controller = new TencentQianWebViewController();

TencentQianWebViewComponent component = new TencentQianWebViewComponent.Builder(this)
        .url(signUrl)
        .config(new TencentQianWebViewConfig.Builder().topBarTitle("电子签署").build())
        .onSignResult(result -> { /* 处理签署结果 */ })
        .onTitleChanged((title, canGoBack) -> { /* 更新自绘顶栏标题 */ })
        .onProgressChanged((progress, visible) -> { /* 显示/更新进度条 */ })
        .onRenderUnrecoverable(() -> { /* 显示错误页 + controller.reload() 重试 */ })
        .controller(controller)
        .reusePrewarmPool(false)    // 默认关闭
        .build();

myLayout.addView(component, new FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT));

// 页面销毁时（Activity onDestroy / Fragment onDestroyView）：component.release();
```

> 宿主 Activity 必须是 `ComponentActivity`（`AppCompatActivity` / `FragmentActivity`）。组件自建自管 WebView，`onDetachedFromWindow` 仅暂停渲染，显式 `release()` 才销毁。
>
> Edge-to-Edge：宿主持有布局，SDK 不主动避让；`setContentView` 后调用 `EdgeToEdge.apply(findViewById(R.id.xxx_root))` 一行接入（系统栏 / 挖孔 / 键盘，见 Demo `ComponentDemoActivity`）。

完整参数面、生命周期与关键约束详见 [../../docs/usage-guide.md §1.5](../../docs/usage-guide.md#15-组件模式嵌入自定义布局)。

### 配置模式

```java
TencentQianWebViewConfigurator.Handle handle = TencentQianWebViewConfigurator.apply(
    activity,
    existingWebView,
    new TencentQianWebViewConfig.Builder()
        .userAgentExtra("YourApp/1.0")
        .build(),
    callback
);

// 退出页面：handle.detachAndDestroy()
// 复用 WebView：handle.detach()
```

> Edge-to-Edge：SDK 不干预宿主布局；`setContentView` 后调用 `EdgeToEdge.apply(findViewById(R.id.xxx_root))` 一行接入（见 Demo `ConfiguratorDemoActivity`）。

### 预热（可选，提升首屏速度）

预热与容器/配置模式**正交**，不预热时行为与现状完全一致，预热是纯增量收益。`configure()` 可省（首次 `prewarm` 会以默认 `PROD` 懒初始化）；`MINIMAL` 仅 DNS、收益小，真正提速靠 `LIGHT` / `HEAVY`，按意图触发即可：

```java
// 进入签署业务入口前（如列表页 / 详情页）
TencentQianWebViewPrewarmer.configure(
        PrewarmConfig.builder().environment(PrewarmEnvironment.PROD).build());
TencentQianWebViewPrewarmer.prewarm(PrewarmLevel.LIGHT, "single-sign", context);
TencentQianWebViewPrewarmer.prewarm(PrewarmLevel.HEAVY, "single-sign", context);

// 签署流程结束、不再需要时主动释放（回收预热态 WebView 占用的内存）
TencentQianWebViewPrewarmer.releaseAll();
```

完整档位语义、触发时机、池生命周期与端差异详见 [../../docs/usage-guide.md §3.5](../../docs/usage-guide.md#35-预热可选提升首屏速度)。

## 与 Kotlin 版本的差异

| 方面 | Kotlin 版本 | Java 版本 |
|---|---|---|
| Config 构造 | 命名参数 + 默认值 | `Builder` 模式 |
| 事件类型判断 | `when (event)` | `if-else instanceof` |
| 密封类 | `sealed class SdkEvent` | `abstract class` + `static final` 子类 |
| 数据类 | `data class` | 手写 POJO（含 `equals` / `hashCode` / `toString`） |
| 内部可见性 | `internal` | package-private（无修饰符） |
| 构建脚本 | Kotlin DSL（`.kts`） | Groovy DSL（`.gradle`） |
| 依赖 | `core-ktx` / `activity-ktx` / `fragment-ktx` | `core` / `activity` / `fragment` |

API 行为、Config 字段语义、`SignResult` 结构、UA 注入格式均与 Kotlin 版完全一致。

## 单元测试

```bash
bash scripts/test-android-java.sh
```

测试报告输出到 `webview-container/build/reports/tests/`。

## 集成与排错

- 接入指南：[../../docs/usage-guide.md](../../docs/usage-guide.md)
- 接入自检清单：[../../docs/integration-checklist.md](../../docs/integration-checklist.md)
- 排错决策树 + FAQ：[../../docs/troubleshooting.md](../../docs/troubleshooting.md)
- `qianapp://` 协议规范：[../../docs/qianapp-protocol.md](../../docs/qianapp-protocol.md)
