0%

WebViewJavascriptBridge学习

反馈请联系hertz@hertzwang.com,谢谢

概述

WebViewJavascriptBridge可用于iOS原生(以下简称原生)与JavaScript(以下简称JS)交互,集成和使用参考README.md

Bridge通过WebView初始化后加载网页,html首次加载时新建一个隐藏的iframe,通过设置scr来触发原生URL拦截从而达到注入JS的目的

原生注册方法供JS调用:JS调用时先将方法名和传参缓存(至sendMessageQueue),再更新iframe.scr触发原生URL拦截(-webView:decidePolicyForNavigationAction:decisionHandler:),原生调用JS方法(WebViewJavascriptBridge._fetchQueue();)获取方法名和传参,解析后从bridge.base.messageHandlers取出对应的Block然后执行

JS注册方法供原生调用:原生通过bridge可直接调用JS方法

文件目录

  • WebViewJavascriptBridge:适用于UIWebView的Bridge,操作系统在MAC_10_9以上或设备为IPHONE_7_1及上中,使用WebViewJavascriptBridgeBase对象做为Bridge
  • WKWebViewJavascriptBridge:适用于WKWebView的Bridge
  • WebViewJavascriptBridgeBase:Bridge的基于组件,用于存储原生注册的方法、封装JS方法的调用等
  • WebViewJavascriptBridge_JS:声名的C方法,内部有注入WebView中的JS代码

原生部分

WebViewJavascriptBridge属性初始化

使用方法[WebViewJavascriptBridge bridgeForWebView:webView]初始化会得到一个实例(以下简称bridge),内部实现如下:

  • weak当前webView
  • 设置webView.navigationDelegate = self
  • 新建WebViewJavascriptBridgeBase属性(以下简称bridgeBase)
    • messageHandlers存储原生注册的方法

注册原生方法-registerHandler:handler:

bridge调用后会存储在bridgeBasemessageHandlers

原生调用JS方法-callHandler:data:

bridge调用后bridgeBase会将方法及参数格式化成WebViewJavascriptBridge._handleMessageFromObjC('%@');后,调用webView-evaluateJavaScript:completionHandler:

这里的WebViewJavascriptBridge是被注入到WebView中JS的window属性,代码如下:

window.WebViewJavascriptBridge = {
    registerHandler: registerHandler,
    callHandler: callHandler,
    disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
    _fetchQueue: _fetchQueue,
    _handleMessageFromObjC: _handleMessageFromObjC,
};

方法_handleMessageFromObjC直接调用_dispatchMessageFromObjC来处理JS的方法调用

JS部分

声名setupWebViewJavascriptBridge方法

在JS中添加以下代码:

function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.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)
}
  • WebViewJavascriptBridge:上述中讲到是window的属性,在注入的JS代码中声名,其包含以下属性:
    • registerHandler:存储JS注册的方法
    • callHandler:调用原生方法(存储调用原生的方法和参数对象至sendMessageQueue,更新iframe.scr触发原生拦截从而调用JS方法从sendMessageQueue中获取对象,原生解析对象后调用相应的方法)
    • _fetchQueue:获取sendMessageQueue中存储的原生方法名和参数
    • _handleMessageFromObjC:直接调用_dispatchMessageFromObjC来处理JS的方法调用
  • WVJBIframe:隐藏的iframe,通过修改它的src来触发原生拦截

注册JS方法bridge.registerHandler

setupWebViewJavascriptBridge(function(bridge) {
    
    /* Initialize your app here */

    bridge.registerHandler('JS Echo', function(data, responseCallback) {
        console.log("JS Echo called with:", data)
        responseCallback(data)
    })
})

JS调用原生方法bridge.callHandler

setupWebViewJavascriptBridge(function(bridge) {
    bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
        console.log("JS received response:", responseData)
    })
})