webView 与 App 交互, 即网页通过 JSBrige 调用 App 的功能, App 也可以通过 JSBrige 调用网页提供的方法. 最近刚好接触到这一块, 记录一下前端侧的实际操作过程, 这篇文章适合还没接触过这一块的同学们, 这里不讲原理, 直接开始实战的过程.
准备工作
与客户端同学沟通好使用的 JSBrige 库, 我这里使用的是下面这两个库:
- iOS(1.1w+ Star):
- Android(6k+ Star): https://github.com/lzyzsd/JsBridge
Star 数量比较高, 使用的企业也比较多, 所以还是比较可靠的, 可以在它们的文档中示例代码.
注意: GitHub 上有很多这样的库, 但最好配套使用, 即 iOS 和 Android 注入的 jsBrige 名称一致, 我们前端使用时比较方便统一.
开发步骤
1. 统一封装 App 注入的 JSBrige
iOS 和 Android 注入的 jsbrige 可能会有些差异, 所以在使用前我们需要抹平不同客户端的差异. 如果 App 的同学使用了上面说的库, 安卓和 iOS 会在 WebView 中的 Windows 下注入一个 WebViewJavascriptBridge 的对象, iOS 有可能是注入 WVJBCallbacks 的数组, 也有可能需要通过 iframe 来注入. iOS 的文档中给出了一个兼容的示例代码:
- function setupWebViewJavascriptBridge(callback) {
- if (Windows.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
- if (Windows.WVJBCallbacks) { return Windows.WVJBCallbacks.push(callback); }
- Windows.WVJBCallbacks = [callback];
- var WVJBIframe = document.createElement('iframe');
- WVJBIframe.style.display = 'none';
- WVJBIframe.src = 'https://__bridge_loaded__';
- document.documentElement.appendChild(WVJBIframe);
- setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
- }
我们可以直接使用上面的代码, 为了使用方便, 在页面 onload 后, 我们将这个函数挂在 Windows 下:
- Windows.onload = function () {
- function setupWebViewJavascriptBridge(callback) {
- if (Windows.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
- if (Windows.WVJBCallbacks) { return Windows.WVJBCallbacks.push(callback); }
- Windows.WVJBCallbacks = [callback];
- var WVJBIframe = document.createElement('iframe');
- WVJBIframe.style.display = 'none';
- WVJBIframe.src = 'https://__bridge_loaded__';
- document.documentElement.appendChild(WVJBIframe);
- setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
- }
- Windows.setupWebViewJavascriptBridge = setupWebViewJavascriptBridge
- }
Windows.setupWebViewJavascriptBridge 这个函数, 就是我们所说的 JSBridge, 即 WebView 与 App 交互的桥梁.
2. JS 调用 App 的方法
App 端的同学需要先用库提供的方法在 App 端实现一个方法, 方法名比如: playMusic(musicId), 可以传递参数 musicId, 表示让 App 开始播放某一首音乐. JS 调用时如:
- Windows.setupWebViewJavascriptBridge(function (bridge) {
- bridge.callHandler('playMusic', { musicId: 1 }, function (data) {
- console.log('app 触发成功了, 音乐正在播放...APP 回调返回的数据:', data)
- })
- })
上面相当于向 App 发起了一个 playMusic 请求, App 收到请求后, 执行相关的动作, 然后可以 callback, 网页可以拿到回调并继续做相关动作 (需要的话).
3. App 调 JS 提供的方法
同样的道理, 若想 App 能调用 JS 提供的方法, JS 中要先注册这个方法, 方法名比如 stateChange(state),JS 注册方法:
- Windows.setupWebViewJavascriptBridge(function (bridge) {
- bridge.registerHandler('stateChange', function (data, responseCallback) {
- console.log('收到 APP 请求 stateChange 事件, 请求的数据是:', data)
- // 可以返回给 App 一个回调
- responseCallback('朕已经收到 APP 爱卿的请求了, 且退下!')
- })
- })
到这里, WebView 与 App 的交互其实就完成了, 就是这么简单.
注意事项
Windows.onload 时就 callHandler 可能不成功
一般在 Android 中会出现这个问题, 这是因为 App 注入的 WebViewJavascriptBridge 还不存在, JS 就调用其中的方法了, 所以就会没有效果. 而 Android 的库也给出了这个注意事项 https://github.com/lzyzsd/JsBridge#notice , 所以得这样写:
- Windows.onload = function () {
- function registerAppEvent () {
- Windows.setupWebViewJavascriptBridge(function (bridge) {
- bridge.registerHandler('stateChange', function (data, responseCallback) {
- console.log('收到 APP 请求, 请求的数据是:', data)
- // 可以返回给 App 一个回调
- responseCallback('朕已经收到 APP 爱卿的请求了, 且退下!')
- })
- })
- }
- // 兼容写法
- if (Windows.WebViewJavascriptBridge) {
- registerAppEvent()
- } else {
- document.addEventListener(
- 'WebViewJavascriptBridgeReady'
- , function() {
- registerAppEvent()
- },
- false
- )
- }
- }
但这里没有考虑到 iOS 的情况, 在最新的 iOS 中 WebViewJavascriptBridge 可能是不存在的, 所以上面写法 registerAppEvent() 在 iOS 可能无法执行. 为了避免多次注入事件, 我这里用了 setTimeout, 兼容两端的流程:
- let isInitBridgeEvent = false // 作为是否已注册过事件的标记
- if (Windows.WebViewJavascriptBridge) {
- registerAppEvent()
- isInitBridgeEvent = true
- } else {
- document.addEventListener(
- 'WebViewJavascriptBridgeReady',
- function () {
- registerAppEvent()
- isInitBridgeEvent = true
- },
- false
- )
- // 如果还没注册则再注册一次
- setTimeout(() => {
- if (!isInitBridgeEvent) {
- registerAppEvent()
- isInitBridgeEvent = true
- }
- }, 100)
这坨代码写的很挫, 让我尴尬癌都犯了, 有没有热心的小伙伴帮我优化下写法 [送花花].
JS 调用安卓后 callback 回调不成功, JS 收不到 App 返回的消息
这个问题其实 GitHub 上有 https://github.com/lzyzsd/JsBridge/issues/81 , 这是安卓 1.0.4 版本没有解决的问题, 最新的代码已经解决了. 解决办法是: 安卓需要引入最新的 master 的代码, 而不要使用 1.0.4 版本的代码.
来源: https://juejin.im/post/5c4fc0726fb9a049dd80b046