關于WebViewJavascriptBridge的功能不多做介紹如绸,有興趣的小伙伴可以搜一下
使用示例
iOS端使用如下
let bridge = WKWebViewJavascriptBridge(for: webView)
//原生調用js
bridge.callHandler("jsFunction", data: [:], responseCallback: { (obj) in
})
//native端注冊方法量承,以供js調用
bridge.registerHandler("nativeFunction") { (data, reponseCallback) in
}
實現(xiàn)原理:
js端有一個處理對象,window.WebViewJavascriptBridge對象鞋诗,原生端也有一個類似的對象WebViewJavascriptBridgeBase膀捷,他們兩個在兩端,分別使用攔截url和注入js的方式削彬,進行通信全庸,他們的主要工作內容是
存儲自己端registed handler(實質是個block數(shù)組)
存儲回調(實質也是個block數(shù)組)
對需要通信數(shù)據(jù)進行編碼和解碼
//js端存儲相關代碼
var messageHandlers = {}; //存儲registerHandler方法注冊過的block
var responseCallbacks = {}; //存儲callHandler中的responseCallback
//原生端存儲相關
@interface WebViewJavascriptBridgeBase : NSObject
@property (strong, nonatomic) NSMutableDictionary* responseCallbacks; //存儲callHandler中的responseCallback
@property (strong, nonatomic) NSMutableDictionary* messageHandlers; //存儲registerHandler方法注冊過的block
@end
初始化過程
原生端的初始化:原生端調用WebViewJavascriptBridge庫,生成需要的WebViewJavascriptBridge或者WKWebViewJavascriptBridge實例
js端的初始化:js端加載地址為“https://__bridge_loaded__”的新頁面融痛,新頁面的加載事件觸發(fā)webview的代理方法
//WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
在代理方法中識別加載地址為“https://__bridge_loaded__”壶笼,調用WebViewJavascriptBridge庫中預先寫好的一段js代碼 — WebViewJavascriptBridge_JS,使用evaluateJavaScript執(zhí)行這段js代碼雁刷,在js端生成window.WebViewJavascriptBridge對象及一些需要的js方法覆劈。
這種交互方式,比較通用的叫法是 url sheme
原生調用js
原生調用js的callHandler方法沛励,實際上是調用WebViewJavascriptBridgeBase中方法對參數(shù)進行處理责语。
WebViewJavascriptBridgeBase會為此次調用的回調生成一個id,并在responseCallbacks中用ID為key存儲回調目派。然后把js方法名坤候、參數(shù)、回調id 組織成字典企蹭,再json字符串化白筹。
{
"data": ["ID": 67788],
"callbackId": "objc_cb_1",
"handlerName": "jsFunction"
}
然后用“WebViewJavascriptBridge._handleMessageFromObjC”把這個message字符串包起來。_handleMessageFromObjC的是初始化時通過WebViewJavascriptBridge_JS注入js中的一個js方法谅摄。接下來通過evaluateJavaScript執(zhí)行handleMessageFromObjC徒河。
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
js端的bridge方法handleMessageFromObjC被調用后,先將json字符串解析成字典對象送漠,然后根據(jù)字典中的handlerName值顽照,在messageHandlers中尋找注冊過的對應name的function,將message.data和js回調傳給該function螺男,進行調用棒厘。
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};
}
var handler = messageHandlers[message.handlerName];
if (!handler) {
console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
} else {
handler(message.data, responseCallback);
}
注意一點,上面的responseCallback是js自己的回調下隧,原生的回調還留在WebViewJavascriptBridge的responseCallbacks字典中存儲。js回調的功能是給原生發(fā)送回調消息谓媒。
js回調調用_doSend方法中把字典類型的message添加進sendMessageQueue數(shù)組
[handlerName: xxxx, responseId: xxx, responseData: xxx] //iOS 字典
{handlerName: xxxx, responseId: xxx, responseData: xxx} //js 字典
然后加載一個地址為“https://__wvjb_queue_message__”的新頁面淆院,原生的代理方法被調用
WebViewJavascriptBridge中的代理方法識別到特殊的加載地址,使用evaluateJavaScript調用“WebViewJavascriptBridge._fetchQueue();”,js端的_fetchQueue把sendMessageQueue數(shù)組中的message都格式化成json字符串土辩。原生拿到messages的json字符串后先把字符串轉換成數(shù)組支救,然后遍歷數(shù)組處理每個message(通常只有一個)。原生bridge發(fā)現(xiàn)message中有responseId拷淘,則用responseId調用對應的responseCallback各墨。如果沒有responseId,說明這不是 原生調js - js回調原生启涯,而是js直接調用原生的方法贬堵,那就從messageHandlers中尋找對應handlerName的block
在原生端初始化WebViewJavascriptBridge的時候,WebViewJavascriptBridge就把webview的代理指向了自己结洼,所以這里能攔截到黎做。如果webview的代理被指向了其他地方,那WebViewJavascriptBridge就不工作了松忍。畢竟js端初始化就指望著這個代理方法攔截到請求蒸殿,然后去給js注入window.WebViewJavascriptBridge對象。
js調用原生
前面原生調用js中說到了原生發(fā)消息給js鸣峭,js怎么處理宏所,原生的回調怎么處理。js調用原生是類似的邏輯摊溶。只是雙方發(fā)消息的方式不一樣爬骤,一個是通過evaluateJavaScript,一個是通過url sheme更扁。這里不再多述盖腕。