Hybrid APP基礎篇(五)->JSBridge實現示例

来源:http://www.cnblogs.com/dailc/archive/2016/10/05/5931328.html
-Advertisement-
Play Games

說明 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了
			

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.先重寫uiviewcontrol的方法 - (UIViewController *)viewController { for (UIView* next = [self superview]; next; next = next.superview) { UIResponder *nextRes ...
  • 概述在動畫中,我們會指定動畫的持續時間。例如scaleAnimation.duration = self.config.appearDuration那麼這個時間是怎麼定義的呢?是指的絕對時間嗎?層級時間結構layer在屏幕上的顯示位置是根據父layer的位置以及本身相對於父layer偏移定義的。與此... ...
  • iOS多線程技術方案 === 目錄 "一、多線程簡介" "1、多線程的由來" "2、耗時操作的模擬試驗" "3、進程和線程" "4、多線程的概念及原理" "5、多線程的優缺點和一個Tip" "6、主線程" "7、技術方案" "二、Pthread" "1、函數" "2、參數和返回值" "3、使用" " ...
  • 眾所周知,viewPager是能夠滑動的,但有時候我們需要禁止它的滑動(微笑地面對*—……—*)。 情況是這樣的: activity中有一個viewPager,viewPager中加入3個Fragment,第三個Fragment中又使用了一個viewPager,這個viewPager中又加入了幾個F ...
  • 【框架】: 公共部分:左側菜單、TitleBar、RadioGroup(3個RadioButton:X、Y、Z) 選擇X頁面:指示器+ViewPager 【要達成的效果】: (1)左側選擇A,進入X頁面,X1聯網刷新頁面,此時禁止X2預載入—>滑動到X2頁面,X2才聯網刷新—>X3—>X4; (2) ...
  • Activty啟動提供了四種啟動模式。launchMode: standard:每次啟動新的活動視窗(new操作) singleTop:如果在棧頂是目標活動,則直接打開.否則開啟新的活動視窗(new). singleTask和singleInstance基本上相同.差別在於若根活動設置為single ...
  • 當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按BACK鍵的時候。 註意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有 ...
  • 本章節主要為之前項目 JXHomepwner 添加照片功能(項目地址)。具體任務就是顯示一個 UIImagePickerController 對象,使用戶能夠為 JXItem 對象拍照並保存。拍攝的照片會和相應的 JXItem 對象建立關聯,當用戶進入某個 JXItem 對象的詳細視圖的時候,可以看 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...