iOS WebViewJavascriptBridge 源碼解析

一. 概述

做客戶端開發免不了要與WebView打交道,特別是對于Hybrid App,在H5所占比重越來越大的背景下,一套好的WebView 與原生交互的API顯得尤為重要,當然目前兩端都有比較成熟的三方庫進行支持。比如Android端的JsBridge,iOS端的WebViewJavascriptBridge,但是對于其內部原理筆者一直一知半解,導致有時面對問題無從下手,最后決心分析WebViewJavascriptBridge的內部實現原理,一是提升自己的源碼閱讀水平,其次也希望對以后的工作有所幫助。

二. 基本原理

image.png

下載WebViewJavascriptBridge的源碼后可以看到其文件并不多,分別對幾個文件做簡單的介紹,后面詳細分析其源碼

  • WebViewJavascriptBridge_JS: JS橋接文件,通過它實現JS環境的初始化,里面就一個C函數,返回的是JS方法。原生調用的JS方法與對應的方法回調都需要先在這里面進行注冊。
  • WKWebViewJavascriptBridgeWebViewJavascriptBridge: WKWebViewUIWebView對應的橋接文件。JS調用的原生方法與對應的方法回調都需要先在這里面進行注冊。
  • WebViewJavascriptBridgeBase: 橋接基礎文件。通過他實現對原生環境的初始化,以及對方法存儲容器的初始化,當然還有對WebViewJavascriptBridge_JS里面JS方法的調用。

三. 源碼解析

大體了解了上面幾個類的作用,我們通過源碼來分析其內部的實現邏輯。我們就以WebViewJavascriptBridgeDemo為例。

1. JS調用OC方法

(1) OC環境初始化與方法注冊

如何實現JS調用OC方法呢,首先要對當前OC環境進行初始化

// ExampleWKWebViewController
_bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
    [_bridge setWebViewDelegate:self];     
    [_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"testObjcCallback called: %@", data);
        responseCallback(@"Response from testObjcCallback");
    }];
....
// WKWebViewJavascriptBridge
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
    WKWebViewJavascriptBridge* bridge = [[self alloc] init];
    [bridge _setupInstance:webView];
    [bridge reset];
    return bridge;
}
....
// WKWebViewJavascriptBridge
- (void) _setupInstance:(WKWebView*)webView {
    _webView = webView;
    _webView.navigationDelegate = self;
    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}
  • [WebViewJavascriptBridge bridgeForWebView:webView];: 看這個方法的調用棧,可以清晰的看到其作用是初始化 WKWebViewJavascriptBridge,進而實例化其對應的WebViewJavascriptBridgeBase,還有綁定各自的代理,最終實現初始化OC調用環境的目的。

  • - (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler; : 如果要實現JS調用原生方法的目的,那么必須對原生方法進行注冊,這個就是對應的注冊方法。我們來看他內部做了什么:

- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}

很簡單,只不過把當前的Block保存進了messageHandlers這個字典中,以便等JS端調用時,通過方法名稱來找到其對應的實現。

(2) JS環境初始化與方法觸發

OC環境初始化與方法注冊完成后,我們來下JS環境的初始化 Demo中通過- (void)loadExamplePage:(WKWebView*)webView 方法加載網頁到當前的webView,來看下ExampleApp.html中的核心方法:

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)
  }
  setupWebViewJavascriptBridge(function(bridge) {
  var uniqueId = 1
  function log(message, data) {
    var log = document.getElementById('log')
    var el = document.createElement('div')
    el.className = 'logLine'
    el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
    if (log.children.length) {  log.insertBefore(el, log.children[0]) }
    else { log.appendChild(el)}
  }
  bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
    log('ObjC called testJavascriptHandler with', data)
    var responseData = { 'Javascript Says':'Right back atcha!' }
    log('JS responding with', responseData)
    responseCallback(responseData)
  })
  document.body.appendChild(document.createElement('br'))
  var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))
  callbackButton.innerHTML = 'Fire testObjcCallback'  callbackButton.onclick = function(e) {
    e.preventDefault()
    log('JS calling handler "testObjcCallback"')
    bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
      log('JS got response', response)
    })
  }
})
  • setupWebViewJavascriptBridge(callback)是核心方法,webView加載html后會首先調用這個方法。這個方法需要一個參數callback,也是一個函數。我們來看這個方法:
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)
}

第一次加載網頁時 window.WebViewJavascriptBridgewindow.WVJBCallbacks都是false,把window.WVJBCallbacks賦值為包含callback的數組,此時callback為一個函數,就是后面的function(bridge) ....,接下來創建WVJBIframe,你可以把它理解為一個空白頁面,創建它的目的是設置src = 'https://__bridge_loaded__';,

注意這個src屬性很關鍵,當我們設置一個網頁的src屬性時,這個鏈接會被我們OC端的webView所捕獲,從而調用webView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler,

后面兩句代碼的的意思是加載當前空白頁,以便觸發OC的代理方法,然后立馬移除。

  • 接下來我們去WKWebViewJavascriptBridge中看 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 這個代理方法攔截到請求后做了什么。
NSURL *url = navigationAction.request.URL;
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;

if ([_base isWebViewJavascriptBridgeURL:url]) {
    if ([_base isBridgeLoadedURL:url]) {
        [_base injectJavascriptFile];
    } else if ([_base isQueueMessageURL:url]) {
        [self WKFlushMessageQueue];
    } else {
        [_base logUnkownMessage:url];
    }
    decisionHandler(WKNavigationActionPolicyCancel);
    return;
}

首先判斷當前的URL是否是__wvjb_queue_message__或者__bridge_loaded__,剛才觸發的URL是 __bridge_loaded__會調用WebViewJavascriptBridgeBase- (void)injectJavascriptFile方法。

- (void)injectJavascriptFile {
    // 獲取JS字符串
    NSString *js = WebViewJavascriptBridge_js();
    [self _evaluateJavascript:js];
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}
....
- (void) _evaluateJavascript:(NSString *)javascriptCommand {
    [self.delegate _evaluateJavascript:javascriptCommand];
}
....
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
    [_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
    return NULL;
}

通過以上方法調用可以看到,最后是把WebViewJavascriptBridge_js(); JS方法字符串,通過方法 [_webView evaluateJavaScript:javascriptCommand completionHandler:nil] 注入到了webView中并且執行。從而達到初始化javascript環境的brige的作用。

  • WebViewJavascriptBridge_js()方法解析
window.WebViewJavascriptBridge = {
  registerHandler: registerHandler,
  callHandler: callHandler,
  disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
  _fetchQueue: _fetchQueue,
  _handleMessageFromObjC: _handleMessageFromObjC
};
var messagingIframe;
  // 要發送給原生的消息列表
var sendMessageQueue = [];
  // 存儲注冊在bridge的JS方法
var messageHandlers = {};
// 要跳轉的
URLvar CUSTOM_PROTOCOL_SCHEME = 'https';
var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
//JS方法回調
var responseCallbacks = {};
var uniqueId = 1;
var dispatchMessagesWithTimeoutSafety = true;
  // OC調用的JS方法需要用它來進行注冊
function registerHandler(handlerName, handler) {
  messageHandlers[handlerName] = handler;
}
//JS調用OC的方法入口function callHandler(handlerName, data, responseCallback) {
  if (arguments.length == 2 && typeof data == 'function') {
    responseCallback = data;
    data = null;
  }
  _doSend({ handlerName:handlerName, data:data }, responseCallback);
}
function disableJavscriptAlertBoxSafetyTimeout() {
  dispatchMessagesWithTimeoutSafety = false;
}
// 要發送消息給原生了
function _doSend(message, responseCallback) {
  if (responseCallback) {
    var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
    responseCallbacks[callbackId] = responseCallback;
    message['callbackId'] = callbackId;
  }
  sendMessageQueue.push(message);
      //觸發webView 代理,解析JS 的message  messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
  // 把消息轉成json字符串function _fetchQueue() {
  var messageQueueString = JSON.stringify(sendMessageQueue);
  sendMessageQueue = [];
  return messageQueueString;
}
function _dispatchMessageFromObjC(messageJSON) {
  ....
}
// 原生會調用他,JS用它來達到消息分發
function _handleMessageFromObjC(messageJSON) {
      _dispatchMessageFromObjC(messageJSON);
}
messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);
setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
  var callbacks = window.WVJBCallbacks;
  delete window.WVJBCallbacks;
  for (var i=0; i<callbacks.length; i++) {
    callbacks[i](WebViewJavascriptBridge);
  }
}

我截選了一些關鍵代碼,首先整個WebViewJavascriptBridge_js是一個JS方法的執行,首先創建了JS端的WebViewJavascriptBridge并賦值給了window,我們來看這個對象的構成:

window.WebViewJavascriptBridge = {
  registerHandler: registerHandler,
  callHandler: callHandler,
  disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
  _fetchQueue: _fetchQueue,
  _handleMessageFromObjC: _handleMessageFromObjC
};
  • registerHandler:直接對應下面的registerHandler(handlerName, handler) 方法,通過它我們把能被OC調用的JS方法進行注冊,看它的實現也是比較簡單的
function registerHandler(handlerName, handler) {
 messageHandlers[handlerName] = handler;
}

把JS的方法實現以方法名handleName保存在messageHandlers中。

  • callHandler: 對應下面callHandler(handlerName, data, responseCallback)方法,通過它我們可以直接發起對OC方法的調用,具體調用邏輯我們在下面進行分析。
  • disableJavscriptAlertBoxSafetyTimeout:回調是否超時開關,默認為false
  • _fetchQueue: 把javascript環境的方法序列化成JSON字符串,并返回給OC端
  • _handleMessageFromObjC:處理OC發給javascript環境的方法,_dispatchMessageFromObjC(messageJSON)這個方法的參數就是OC調用JS的message信息,這個方法對messageJSON進行解析處理,進而調用相應的JS方法。
messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);

這里的src就是https://wvjb_queue_message,這段代碼的意思是把javascript要發送給OC的消息立即發送出去。

setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
  var callbacks = window.WVJBCallbacks;
  delete window.WVJBCallbacks;
  for (var i=0; i<callbacks.length; i++) {
    callbacks[i](WebViewJavascriptBridge);
  }
}

WebViewJavascriptBridge_js的最后是上面的代碼,它會調用ExampleApp.html中的callBack方法,也就是它

setupWebViewJavascriptBridge(function(bridge) {
 ....
})

繼而完成對這個JS環境的初始化與ExampleApp.html的加載。

(3) JS調用OC方法流程

  • 點擊JS按鈕觸發下面的方法
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
 log('JS got response', response)
})

傳遞方法名testObjcCallback,消息參數{'foo': 'bar'},以及OC回調JS的方法function(response) {log('JS got response', response)}),

  • 調用WebViewJavascriptBridge_js
function callHandler(handlerName, data, responseCallback) {
  if (arguments.length == 2 && typeof data == 'function') {
    responseCallback = data;
    data = null;
  }
  _doSend({ handlerName:handlerName, data:data }, responseCallback);
}
....
function _doSend(message, responseCallback) {
  if (responseCallback) {
    var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
    responseCallbacks[callbackId] = responseCallback;
    message['callbackId'] = callbackId;
  }
  sendMessageQueue.push(message);
  messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}

可以看到核心方法是_doSend(),入參message是調用OC的方法名與參數,responseCallback是OC回調JS的方法,接下來將這個回調方法保存在responseCallbacks中,key值是callbackId,消息對象message也添加一個callbackId,最后設置messagingIframe的src屬性,從而被ebView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler攔截。

  • 在上面的代理方法中,攔截到的URL為__wvjb_queue_message__,所以調用方法:
- (void)WKFlushMessageQueue {
    [_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
        if (error != nil) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
        }
        [_base flushMessageQueue:result];
    }];
}
....
- (NSString *)webViewJavascriptFetchQueyCommand {
    return @"WebViewJavascriptBridge._fetchQueue();";
}

WebView觸發JS的WebViewJavascriptBridge._fetchQueue(),

function _fetchQueue() {
 var messageQueueString = JSON.stringify(sendMessageQueue);
 sendMessageQueue = [];
 return messageQueueString;
}

這個方法里面會將sendMessageQueue換成json字符串,然后返回給OC環境,觸發[_base flushMessageQueue:result];

- (void)flushMessageQueue:(NSString *)messageQueueString{
    if (messageQueueString == nil || messageQueueString.length == 0) {
        NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
        return;
    }
    // JS傳遞過來的json字符串,我們進行反序列化 得到message數組    
NSLog(@"messageQueueString===%@",messageQueueString);
    id messages = [self _deserializeMessageJSON:messageQueueString];
    for (WVJBMessage* message in messages) {
        if (![message isKindOfClass:[WVJBMessage class]]) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
            continue;
        }
        [self _log:@"RCVD" json:message];
                NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]); 
           [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) { // 有回調
                responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }
                       
                 WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };
            } else {
                responseCallback = ^(id ignoreResponseData) {
                    // Do nothing
                };
            }
            WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
            if (!handler) {
                NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }
            //向下傳遞參數,并且觸發block回調()
            handler(message[@"data"], responseCallback);
        }
    }
}

這個方法就是OC端處理JS的核心方法了,將messageQueueString反序列化,得到消息數組

(
       {
       callbackId = "cb_1_1639553291614";
       data =         {
           foo = bar;
       };
       handlerName = testObjcCallback;
   }
)

callbackId,表明有消息回調,生成responseCallbackBlock,這個Block里面將接收的參數與callbackId打包一并發送給JS環境,并調用JS環境的WebViewJavascriptBridge._handleMessageFromObjC(messageJSON);方法將messageJSON進行解析。這里只是Block的實現,并沒調用這個Block,調用在下面

WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
if (!handler) {
    NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
    continue;
}
//向下傳遞參數,并且觸發block回調()
handler(message[@"data"], responseCallback);

根據handlerName找到在messageHandlers保存的方法實現,handler(message[@"data"], responseCallback);進行真正的調用,在OC的注冊方法中,調用responseCallback(@"Response from testObjcCallback"); 向JS環境發送回調并傳遞參數。

  • JS環境通過這個_handleMessageFromObjC(messageJSON)方法得到messageJSON,并對其解析。
function _dispatchMessageFromObjC(messageJSON) {
  if (dispatchMessagesWithTimeoutSafety) {
    setTimeout(_doDispatchMessageFromObjC);
  } else {
     _doDispatchMessageFromObjC();
  }
  function _doDispatchMessageFromObjC() {
  //轉換為對象    
var message = JSON.parse(messageJSON);
    var messageHandler;
    var responseCallback;
    // 這個responseId就是JS調用OC方法保存的callbackId
    if (message.responseId) {
      responseCallback = responseCallbacks[message.responseId];
      if (!responseCallback) {
        return;
      }
      responseCallback(message.responseData);
      delete responseCallbacks[message.responseId];
    } else {
      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);
      }
    }
  }
}

先將字符串轉化為JSON對象,根據responseId(這個responseId就是JS調用OC方法保存的callbackId),找到對應的方法實現,進行調用

    function(response) {
  log('JS got response', response)
}

到此就完成了JS調用OC,并且OC回調JS并傳遞參數的全部過程。

2. OC調用JS方法

跟上面類似再來看下OC主動調用JS方法的實現

[_bridge callHandler:@"testJavascriptHandler" data:@{ @"foo":@"before ready" }];
....
 - (void)callHandler:(NSString *)handlerName data:(id)data {
    [self callHandler:handlerName data:data responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
....
  - (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    NSMutableDictionary* message = [NSMutableDictionary dictionary];
        if (data) {
        message[@"data"] = data;
    }
        if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }
        if (handlerName) {
        message[@"handlerName"] = handlerName;
    }
    [self _queueMessage:message];
}
....
  - (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
        NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
    NSLog(@"javascriptCommand==%@",javascriptCommand);
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];
    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}

可以看到,跟JS調用OC方法的原理類似,將OC調用JS的方法名與參數封裝進message對象,如果有回調函數,將回調函數通過responseCallbacks保存,并生成callbackId,將整個message打包發送給JS環境的WebViewJavascriptBridge._handleMessageFromObjC(messageJSON); 進行解析,解析流程上面介紹過了,這里不再贅述。

四. 總結

通過上面流程分析,整個WebViewJavascriptBridge 內部的實現原理就比較清晰了。

  • JS將方法注冊到JS環境的bridge,OC調用JS的核心方法就是[_webView evaluateJavaScript:javascriptCommand completionHandler:nil];,JS環境收到消息后,通過方法WebViewJavascriptBridge._handleMessageFromObjC();將消息進行解析,調用注冊在bridge的方法。
  • OC將方法注冊到OC環境的bridge,JS調用OC的核心邏輯是,設置空白網頁的src屬性,從而被webView的代理方法- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler,OC通過核心方法- (void)flushMessageQueue:(NSString *)messageQueueString將傳遞數據進行解析。
  • 兩邊都是通過方法名找到對應的方法實現,然后通過ID來查找回調函數。
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容