說明 JSBridge實現示例 目錄 前言 參考來源 楔子 JS實現部分 說明 實現 Android實現部分 說明 JSBridge類 實現 Callback類 實現 Webview容器關鍵代碼 實現 API 類實現 iOS實現部分 說明 WebViewJavascriptBridgeBase 實現 ...
說明
JSBridge實現示例
目錄
前言
參考來源
前人栽樹,後臺乘涼,本文參考了以下來源
楔子
本文介紹JSBridge的完整實現,包括JS部分,Android原生,iOS原生部分
JS實現部分
說明
這是一份剔除了業務之後的JSbridge實現代碼(JS部分)。JS實現代碼就一套
實現
實現代碼如下
(function() { (function() { var hasOwnProperty = Object.prototype.hasOwnProperty; var JSBridge = window.JSBridge || (window.JSBridge = {}); //jsbridge協議定義的名稱 var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge'; //最外層的api名稱 var API_Name = 'namespace_bridge'; //進行url scheme傳值的iframe var messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name; document.documentElement.appendChild(messagingIframe); //定義的回調函數集合,在原生調用完對應的方法後,會執行對應的回調函數id var responseCallbacks = {}; //唯一id,用來確保每一個回調函數的唯一性 var uniqueId = 1; //本地註冊的方法集合,原生只能調用本地註冊的方法,否則會提示錯誤 var messageHandlers = {}; //當原生調用H5註冊的方法時,通過回調來調用(也就是變為了非同步執行,加強安全性) var dispatchMessagesWithTimeoutSafety = true; //本地運行中的方法隊列 var sendMessageQueue = []; //實際暴露給原生調用的對象 var Inner = { /** * @description 註冊本地JS方法通過JSBridge給原生調用 * 我們規定,原生必須通過JSBridge來調用H5的方法 * 註意,這裡一般對本地函數有一些要求,要求第一個參數是data,第二個參數是callback * @param {String} handlerName 方法名 * @param {Function} handler 對應的方法 */ registerHandler: function(handlerName, handler) { messageHandlers[handlerName] = handler; }, /** * @description 調用原生開放的方法 * @param {String} handlerName 方法名 * @param {JSON} data 參數 * @param {Function} callback 回調函數 */ callHandler: function(handlerName, data, callback) { //如果沒有 data if(arguments.length == 3 && typeof data == 'function') { callback = data; data = null; } _doSend({ handlerName: handlerName, data: data }, callback); }, /** * iOS專用 * @description 當本地調用了callHandler之後,實際是調用了通用的scheme,通知原生 * 然後原生通過調用這個方法來獲知當前正在調用的方法隊列 */ _fetchQueue: function() { var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; return messageQueueString; }, /** * @description 原生調用H5頁面註冊的方法,或者調用回調方法 * @param {String} messageJSON 對應的方法的詳情,需要手動轉為json */ _handleMessageFromNative: function(messageJSON) { setTimeout(_doDispatchMessageFromNative); /** * @description 處理原生過來的方法 */ function _doDispatchMessageFromNative() { var message; try { message = JSON.parse(messageJSON); } catch(e) { //TODO handle the exception console.error("原生調用H5方法出錯,傳入參數錯誤"); return; } //回調函數 var responseCallback; if(message.responseId) { //這裡規定,原生執行方法完畢後準備通知h5執行回調時,回調函數id是responseId responseCallback = responseCallbacks[message.responseId]; if(!responseCallback) { return; } //執行本地的回調函數 responseCallback(message.responseData); delete responseCallbacks[message.responseId]; } else { //否則,代表原生主動執行h5本地的函數 if(message.callbackId) { //先判斷是否需要本地H5執行回調函數 //如果需要本地函數執行回調通知原生,那麼在本地註冊回調函數,然後再調用原生 //回調數據有h5函數執行完畢後傳入 var callbackResponseId = message.callbackId; responseCallback = function(responseData) { //預設是調用EJS api上面的函數 //然後接下來原生知道scheme被調用後主動獲取這個信息 //所以原生這時候應該會進行判斷,判斷對於函數是否成功執行,並接收數據 //這時候通訊完畢(由於h5不會對回調添加回調,所以接下來沒有通信了) _doSend({ handlerName: message.handlerName, responseId: callbackResponseId, responseData: responseData }); }; } //從本地註冊的函數中獲取 var handler = messageHandlers[message.handlerName]; if(!handler) { //本地沒有註冊這個函數 } else { //執行本地函數,按照要求傳入數據和回調 handler(message.data, responseCallback); } } } } }; /** * @description JS調用原生方法前,會先send到這裡進行處理 * @param {JSON} message 調用的方法詳情,包括方法名,參數 * @param {Function} responseCallback 調用完方法後的回調 */ function _doSend(message, responseCallback) { if(responseCallback) { //取到一個唯一的callbackid var callbackId = Util.getCallbackId(); //回調函數添加到集合中 responseCallbacks[callbackId] = responseCallback; //方法的詳情添加回調函數的關鍵標識 message['callbackId'] = callbackId; } var uri; //android中,可以通過onJsPrompt或者截取Url訪問都行 var ua = navigator.userAgent; if(ua.match(/(iPhone\sOS)\s([\d_]+)/)||ua.match(/(iPad).*OS\s([\d_]+)/)) { //ios中,通過截取客戶端url訪問 //因為ios可以不暴露scheme,而是由原生手動獲取 //正在調用的方法詳情添加進入消息隊列中,原生會主動獲取 sendMessageQueue.push(message); uri = Util.getUri(); }else{ //android中相容處理,將所有的參數一起拼接到url中 uri = Util.getUri(message); } //獲取 觸發方法的url scheme //採用iframe跳轉scheme的方法 messagingIframe.src = uri; } var Util = { getCallbackId: function() { //如果無法解析埠,可以換為Math.floor(Math.random() * (1 << 30)); return 'cb_' + (uniqueId++) + '_' + new Date().getTime(); }, //獲取url scheme //第二個參數是相容android中的做法 //android中由於原生不能獲取JS函數的返回值,所以得通過協議傳輸 getUri: function(message) { var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name; if(message) { //回調id作為埠存在 var callbackId, method, params; if(message.callbackId) { //第一種:h5主動調用原生 callbackId = message.callbackId; method = message.handlerName; params = message.data; } else if(message.responseId) { //第二種:原生調用h5後,h5回調 //這種情況下需要原生自行分析傳過去的port是否是它定義的回調 callbackId = message.responseId; method = message.handlerName; params = message.responseData; } //參數轉為字元串 params = this.getParam(params); //uri 補充 uri += ':' + callbackId + '/' + method + '?' + params; } return uri; }, getParam: function(obj) { if(obj && typeof obj === 'object') { return JSON.stringify(obj); } else { return obj || ''; } } }; for(var key in Inner) { if(!hasOwnProperty.call(JSBridge, key)) { JSBridge[key] = Inner[key]; } } })(); //註冊一個測試函數 JSBridge.registerHandler('testH5Func', function(data, callback) { alert('測試函數接收到數據:' + JSON.stringify(data)); callback && callback('測試回傳數據...'); }); /* ***************************API******************************************** * 開放給外界調用的api * */ window.jsapi = {}; /** ***app 模塊 * 一些特殊操作 */ jsapi.app = { /** * @description 測試函數 */ testNativeFunc: function() { //調用一個測試函數 JSBridge.callHandler('testNativeFunc', {}, function(res) { callback && callback(res); }); } }; })();
Android實現部分
說明
這是Android原生中配套的JSBridge實現代碼。Android的實現相對比JS複雜,包括多個部分
JSBridge類實現
實現代碼如下
public class JSBridge { private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>(); //原生註冊API方法 public static void register(String exposedName, Class<? extends IBridge> clazz) { if (!exposedMethods.containsKey(exposedName)) { try { exposedMethods.put(exposedName, getAllMethod(clazz)); } catch (Exception e) { e.printStackTrace(); } } } //得到所有的註冊方法 private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception { HashMap<String, Method> mMethodsMap = new HashMap<>(); Method[] methods = injectedCls.getDeclaredMethods(); for (Method method : methods) { String name; if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null) { continue; } Class[] parameters = method.getParameterTypes(); if (null != parameters && parameters.length == 4) { if (parameters[0] == BaseWebLoader.class && parameters[1] == WebView.class && parameters[2] == JSONObject.class && parameters[3] == Callback.class) { mMethodsMap.put(name, method); } } } return mMethodsMap; } //調用Hava中的方法 //其中BaseWebLoader是JSBridge的webview容器(二次封裝) //執行完方法後,如果又回到,自動就會調用 public static String callJava(BaseWebLoader webLoader,WebView webView, String uriString) { String methodName = ""; String className = ""; String param = "{}"; String port = ""; if (!TextUtils.isEmpty(uriString) && uriString.startsWith("EpointJSBridge")) { Uri uri = Uri.parse(uriString); className = uri.getHost(); param = uri.getQuery(); port = uri.getPort() + ""; String path = uri.getPath(); if (!TextUtils.isEmpty(path)) { methodName = path.replace("/", ""); } } if (exposedMethods.containsKey(className)) { HashMap<String, Method> methodHashMap = exposedMethods.get(className); if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) { Method method = methodHashMap.get(methodName); if (method != null) { try { method.invoke(null,webLoader, webView, new JSONObject(param), new Callback(webView, port)); } catch (Exception e) { e.printStackTrace(); } } } } return null; } }
這個類的作用是原生定義一些暴露的api
Callback類實現
實現代碼如下
public class Callback { private static Handler mHandler = new Handler(Looper.getMainLooper()); private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge._handleMessageFromNative(%s);"; private String mPort; private WeakReference<WebView> mWebViewRef; public Callback(WebView view, String port) { mWebViewRef = new WeakReference<>(view); mPort = port; } public void apply(JSONObject jsonObject) throws JSONException { JSONObject object = new JSONObject(); object.put("responseId", mPort); object.putOpt("responseData", jsonObject); final String execJs = String.format(CALLBACK_JS_FORMAT, String.valueOf(object)); //如果activity已經關閉則不回調 if (mWebViewRef != null && mWebViewRef.get() != null && !((BaseWebLoader) mWebViewRef.get().getContext()).getActivity().isFinishing()) { mHandler.post(new Runnable() { @Override public void run() { mWebViewRef.get().loadUrl(execJs); } }); } } }
這個類的作用是,定義原生中的回調函數
Webview容器關鍵代碼實現
實現代碼如下
註冊api方法
//定義api集合 JSBridge.register("namespace_bridge",BridgeImpl.class);
捕獲url scheme並執行方法的代碼
public boolean shouldOverrideUrlLoading(WebView view, String url){ //讀取到url後通過callJava分析調用 JSBridge.callJava(BaseWebLoader.this,view,url); //如果返回false,則WebView處理鏈接url,如果返回true,代表WebView根據程式來執行url return true; }
裡面的關鍵代碼是註冊函數,捕獲url,執行方法等
API 類實現
實現代碼如下
public class BridgeImpl implements IBridge { /** * 測試原生方法 */ public static void testNativeFunc(final BaseWebLoader webLoader, WebView wv, JSONObject param, final Callback callback) { //可以這樣獲取參數 param.optString(鍵值); //在一個新的線程內執行 new Thread(new Runnable() { @Override public void run() { try { //執行一些自己的內容 JSONObject object = new JSONObject(); //添加測試信息 object.put("test", "test"); //執行回調 callback.apply(getJSONObject(1, "", object)); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } }
這個類是一些api的具體實現,webview裡面就是註冊了這些對應的api
iOS實現部分
說明
這是iOS原生中配套的JSBridge實現代碼。iOS中代碼是基於UIWebview的,來源於一個github上的開源項目,地址 marcuswestin/WebViewJavascriptBridge
WebViewJavascriptBridgeBase 實現
實現代碼如下
@implementation WebViewJavascriptBridgeBase { __weak id _webViewDelegate; long _uniqueId; } static bool logging = false; static int logMaxLength = 500; + (void)enableLogging { logging = true; } + (void)setLogMaxLength:(int)length { logMaxLength = length;} -(id)init { self = [super init]; self.messageHandlers = [NSMutableDictionary dictionary]; self.startupMessageQueue = [NSMutableArray array]; self.responseCallbacks = [NSMutableDictionary dictionary]; _uniqueId = 0; return(self); } - (void)dealloc { self.startupMessageQueue = nil; self.responseCallbacks = nil; self.messageHandlers = nil; } - (void)reset { self.startupMessageQueue = [NSMutableArray array]; self.responseCallbacks = [NSMutableDictionary dictionary]; _uniqueId = 0; } - (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)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; } 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; } handler(message[@"data"], responseCallback); } } } /*這段代碼在本文中沒有用到 * 因為再原項目中,JSBridge的js庫是放在iOS本地沙盒中的,所以才需要手動註入 * 但是本文中的示例,JSBridge是直接在Html中引用的,所以無需註入 - (void)injectJavascriptFile { 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]; } } } */ -(BOOL)isCorrectProcotocolScheme:(NSURL*)url { if([[url scheme] isEqualToString:kCustomProtocolScheme]){ return YES; } else { return NO; } } -(BOOL)isQueueMessageURL:(NSURL*)url { if([[url host] isEqualToString:kQueueHasMessage]){ return YES; } else { return NO; } } -(BOOL)isBridgeLoadedURL:(NSURL*)url { return ([[url scheme] isEqualToString:kCustomProtocolScheme] && [[url host] isEqualToString:kBridgeLoaded]); } -(void)logUnkownMessage:(NSURL*)url { NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]); } -(NSString *)webViewJavascriptCheckCommand { return @"typeof WebViewJavascriptBridge == \'object\';"; } -(NSString *)webViewJavascriptFetchQueyCommand { return @"JSBridge._fetchQueue();"; } - (void)disableJavscriptAlertBoxSafetyTimeout { [self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"]; } // Private // ------------------------------------------- - (void) _evaluateJavascript:(NSString *)javascriptCommand { [self.delegate _evaluateJavascript:javascriptCommand]; } - (void)_queueMessage:(WVJBMessage*)message { // if (self.startupMessageQueue) { // [self.startupMessageQueue addObject:message]; // } else { // [self _dispatchMessage:message]; // } [self _dispatchMessage: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:@"JSBridge._handleMessageFromNative('%@');", messageJSON]; if ([[NSThread currentThread] isMainThread]) { [self _evaluateJavascript:javascriptCommand]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ [self _evaluateJavascript:javascriptCommand]; }); } } - (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{ return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding]; } - (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON { return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil]; } - (void)_log:(NSString *)action json:(id)json { if (!logging) { return; } if (![json isKindOfClass:[NSString class]]) { json = [self _serializeMessage:json pretty:YES]; } if ([json length] > logMaxLength) { NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]); } else { NSLog(@"WVJB %@: %@", action, json); } } @end
WebViewJavascriptBridgeBase是JSBridge邏輯代碼的基礎支持,也就是說這裡面封裝一些基礎的代碼,供JSBridge內部調用
WebViewJavascriptBridge 實現
實現代碼如下
#if __has_feature(objc_arc_weak) #define WVJB_WEAK __weak #else #define WVJB_WEAK __unsafe_unretained #endif @implementation WebViewJavascriptBridge { WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView; WVJB_WEAK id _webViewDelegate; long _uniqueId; WebViewJavascriptBridgeBase *_base; } /* API *****/ + (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; } + (void)setLogMaxLength:(int)length { [WebViewJavascriptBridgeBase setLogMaxLength:length]; } + (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView { WebViewJavascriptBridge* bridge = [[self alloc] init]; [bridge _platformSpecificSetup:webView]; return bridge; } - (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate { _webViewDelegate = webViewDelegate; } - (void)send:(id)data { [self send:data responseCallback:nil]; } - (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback { [_base sendData:data responseCallback:responseCallback handlerName:nil]; } - (void)callHandler:(NSString *)handlerName { [self callHandler:handlerName data:nil responseCallback:nil]; } - (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)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler { _base.messageHandlers[handlerName] = [handler copy]; } - (void)disableJavscriptAlertBoxSafetyTimeout { [_base disableJavscriptAlertBoxSafetyTimeout]; } /* Platform agnostic internals *****************************/ - (void)dealloc { [self _platformSpecificDealloc]; _base = nil; _webView = nil; _webViewDelegate = nil; } - (NSString*) _evaluateJavascript:(NSString*)javascriptCommand { return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand]; } /* Platform specific internals: OSX **********************************/ #if defined WVJB_PLATFORM_OSX - (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { _webView = webView; _webView.policyDelegate = self; _base = [[WebViewJavascriptBridgeBase alloc] init]; _base.delegate = self; } - (void) _platformSpecificDealloc { _webView.policyDelegate = nil; } - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { if (webView != _webView) { return; } NSURL *url = [request URL]; if ([_base isCorrectProcotocolScheme:url]) { if ([_base isBridgeLoadedURL:url]) { [_base injectJavascriptFile]; } else if ([_base isQueueMessageURL:url]) { NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; [_base flushMessageQueue:messageQueueString]; } else { [_base logUnkownMessage:url]; } [listener ignore]; } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) { [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener]; } else { [listener use]; } } /* Platform specific internals: iOS **********************************/ #elif defined WVJB_PLATFORM_IOS - (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView { _webView = webView; _webView.delegate = self; _base = [[WebViewJavascriptBridgeBase alloc] init]; _base.delegate = self; } - (void) _platformSpecificDealloc { _webView.delegate = nil; } - (void)webViewDidFinishLoad:(UIWebView *)webView { if (webView != _webView) { return; } __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) { [strongDelegate webViewDidFinishLoad:webView]; } } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { if (webView != _webView) { return; } __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) { [strongDelegate webView:webView didFailLoadWithError:error]; } } //捕獲url,併進行分析 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (webView != _webView) { return YES; } NSURL *url = [request URL]; __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; if ([_base isCorrectProcotocolScheme:url]) { if ([_base isBridgeLoadedURL:url]) { [_base injectJavascriptFile]; } else if ([_base isQueueMessageURL:url]) { NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]]; [_base flushMessageQueue:messageQueueString]; } else { [_base logUnkownMessage:url]; } return NO; } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; } else { return YES; } } - (void)webViewDidStartLoad:(UIWebView *)webView { if (webView != _webView) { return; } __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate; if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { [strongDelegate webViewDidStartLoad:webView]; } } #endif @end
WebViewJavascriptBridge是主要的JSBridge邏輯實現代碼
Webview容器關鍵代碼 實現
實現代碼如下
建立JSBridge橋梁
- (void)viewDidLoad { [super viewDidLoad]; // 一些其它操作 // WebViewJavascriptBridge // 給webView建立JS和OC的溝通橋梁 [WebViewJavascriptBridge enableLogging]; self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wv]; [self.bridge setWebViewDelegate:self]; [self registAPIForJS]; }
註冊API方法
- (void)registAPIForJS { // 測試原生方法 [self.bridge registerHandler:@"testNativeFunc" handler:^(id data, WVJBResponseCallback responseCallback) { //可以這樣獲取參數 NSString *str = data[@"key值"]; //可以做一些自己的事情 }]; }
捕獲url scheme並執行方法的代碼
//關鍵是這句代碼 [self.bridge setWebViewDelegate:self]; //然後再WebViewJavascriptBridge里就可以重新shouldStartLoadWithRequest來捕獲url了