4. WebView容器封装与统一接口设计
当Hybrid应用从单页面Demo走向多业务、多端复用时,直接在每个页面里调用WebView、addJavascriptInterface或evaluateJavascript会迅速失控:iOS与Android行为不一致、JSBridge方法散落各处、原生能力升级后旧页面崩溃、H5无法判断当前容器是否支持某个接口。容器封装的目标,就是把“WebView + 原生能力 + 通信协议”收敛为一个稳定、可版本化、可多端对齐的中间层。
容器层的职责划分
一个可维护的容器层通常分为四层,每层只解决一类问题:
- 宿主层(Host):Activity、ViewController、页面路由、生命周期、权限申请。它决定“什么时候创建容器、什么时候销毁”。
- 容器层(Container):WebView实例管理、配置项(UA、缓存、DOM存储、混合内容、调试开关)、加载拦截、错误页、进度条、返回键处理。它决定“WebView以什么姿态运行”。
- 桥接层(Bridge):JSBridge协议解析、方法注册、参数序列化、回调分发、线程切换、超时与异常兜底。它决定“JS与原生如何对话”。
- 能力层(Capability):相机、定位、支付、文件、剪贴板、设备信息等具体原生能力实现。它决定“能做什么”。
分层的关键收益是:能力层不感知WebView,桥接层不感知具体业务,容器层不感知具体能力。新增一个“扫码”能力时,只需在能力层注册实现,桥接层自动暴露,容器层无需改动。
JSBridge接口抽象
JSBridge的本质是一套跨语言RPC协议。设计时不要暴露addJavascriptInterface或WKScriptMessageHandler的原始形态,而应抽象为统一的调用语义:
// H5侧统一调用入口
window.bridge.invoke('camera.scan', {
quality: 'high',
format: 'qr'
}, function(result) {
if (result.code === 0) {
console.log('扫码结果:', result.data.text);
} else {
console.warn('扫码失败:', result.message);
}
});
// 事件订阅(原生主动通知H5)
window.bridge.on('network.change', function(payload) {
console.log('网络类型:', payload.type);
});
协议层需要统一三件事:方法命名采用模块.动作格式(如camera.scan、pay.request);参数结构统一为对象,避免位置参数;回调约定统一为{ code, data, message },其中code=0表示成功。这样H5侧只需一套调用代码,iOS与Android各自实现桥接适配即可。
原生能力注入方式
Android侧常用addJavascriptInterface注入一个NativeBridge对象,但需注意API 17以上必须加@JavascriptInterface注解,且注入对象的方法会在WebView的JavaBridge线程执行,不能直接操作UI。更安全的做法是:注入对象只负责接收消息,实际能力调用通过Handler切换到主线程或工作线程执行。
iOS侧WKWebView使用WKScriptMessageHandler接收JS消息,通过evaluateJavaScript回调结果。由于WKWebView不支持同步返回,所有调用必须设计为异步回调模式。两端注入方式不同,但暴露给H5的window.bridge对象应完全一致。
onPageStarted或didStartProvisionalNavigation时完成,确保H5首屏脚本执行前window.bridge已存在。若H5在DOMContentLoaded之前调用,需在JS侧做就绪检测或队列缓冲。
多端一致性设计
多端一致性不是“代码相同”,而是“行为相同”。需要对齐的维度包括:方法名与参数结构、回调时机与顺序、错误码语义、超时时间、并发调用限制。例如camera.scan在Android上可能返回code=0并携带data.text,iOS也必须返回相同结构,不能一端返回字符串、另一端返回对象。
建议维护一份接口契约表,作为H5、iOS、Android三端共同遵守的“合同”。契约表至少包含:方法名、参数schema、返回值schema、错误码列表、最低支持版本。任何一端修改契约,必须同步更新版本号并通知另外两端。
接口版本管理与兼容策略
版本管理解决的是“旧H5遇到新容器”和“新H5遇到旧容器”两个方向的问题。推荐采用语义化版本:主版本号表示不兼容变更,次版本号表示新增能力,修订号表示修复。桥接层提供bridge.version()返回当前协议版本,H5在启动时读取并决定降级路径。
| 场景 | 判断依据 | 处理策略 |
|---|---|---|
| 新H5调用旧容器不支持的接口 | bridge.version()低于接口最低版本 |
H5降级到Web实现或隐藏入口 |
| 旧H5调用已废弃接口 | 容器侧标记deprecated |
容器保留兼容实现,输出警告日志 |
| 参数结构变更 | 主版本号升级 | 容器同时支持新旧参数解析,按版本分流 |
| 能力被移除 | 主版本号升级且无兼容层 | H5必须升级,容器返回明确错误码 |
兼容策略的核心原则是:新增能力不破坏旧调用,废弃能力先标记后移除。具体做法是容器侧维护一个能力注册表,每个能力声明minVersion和deprecatedVersion。当H5调用时,桥接层先检查版本,若低于minVersion则返回ERR_UNSUPPORTED;若高于deprecatedVersion则返回ERR_DEPRECATED并附带替代方法名。
最后,容器封装与接口设计的验收标准可以归纳为三条:H5侧只依赖window.bridge一个入口;新增原生能力不需要修改H5调用代码;任意一端升级后,另外两端在版本检查通过的前提下行为不变。做到这三点,Hybrid容器才真正具备生产级的稳定性和可演进性。