The choice of Hybrid APP hybrid development mode (7: full version of JSBridge)

Keywords: JSON iOS Android Javascript

preface

Reference sources

This paper refers to the following sources

Wedge

This paper introduces the complete implementation of JSBridge, including JS part, Android native part and iOS native part

JS implementation part

explain

This is a JSbridge implementation code (JS part) after excluding the business. JS implementation code on a set

realization

The implementation code is as follows

(function() {
	(function() {
		var hasOwnProperty = Object.prototype.hasOwnProperty;
		var JSBridge = window.JSBridge || (window.JSBridge = {});
		//The name of the jsbridge protocol definition
		var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge';
		//The name of the outermost api
		var API_Name = 'namespace_bridge';
		//iframe for url scheme value transfer
		var messagingIframe = document.createElement('iframe');
		messagingIframe.style.display = 'none';
		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
		document.documentElement.appendChild(messagingIframe);

		//After the corresponding method is called, the corresponding callback function id will be executed
		var responseCallbacks = {};
		//Unique id, used to ensure the uniqueness of each callback function
		var uniqueId = 1;
		//Locally registered method collection. Native methods can only call locally registered methods, otherwise an error will be prompted
		var messageHandlers = {};
		//When a method registered in H5 is called natively, it is called through callback (that is, it is changed into asynchronous execution to enhance security)
		var dispatchMessagesWithTimeoutSafety = true;
		//Method queue in local running
		var sendMessageQueue = [];

		//Objects actually exposed to native calls
		var Inner = {
			/**
			 * @description Register local JS method to call native through JSBridge
			 * We stipulate that the native must call the H5 method through JSBridge
			 * Note that there are some requirements for local functions. The first parameter is data and the second parameter is callback
			 * @param {String} handlerName Method name
			 * @param {Function} handler Corresponding methods
			 */
			registerHandler: function(handlerName, handler) {
				messageHandlers[handlerName] = handler;
			},
			/**
			 * @description Calling native open methods
			 * @param {String} handlerName Method name
			 * @param {JSON} data parameter
			 * @param {Function} callback Callback function
			 */
			callHandler: function(handlerName, data, callback) {
				//If there is no data
				if(arguments.length == 3 && typeof data == 'function') {
					callback = data;
					data = null;
				}
				_doSend({
					handlerName: handlerName,
					data: data
				}, callback);
			},
			/**
			 * iOS special-purpose
			 * @description When the callHandler is called locally, the generic scheme is actually called to notify the native
			 * This method is then called natively to know the method queue that is currently being called
			 */
			_fetchQueue: function() {
				var messageQueueString = JSON.stringify(sendMessageQueue);
				sendMessageQueue = [];
				return messageQueueString;
			},
			/**
			 * @description Call the registered method of H5 page, or call the callback method
			 * @param {String} messageJSON The details of the corresponding method need to be manually converted to json
			 */
			_handleMessageFromNative: function(messageJSON) {
				setTimeout(_doDispatchMessageFromNative);
				/**
				 * @description Dealing with native methods
				 */
				function _doDispatchMessageFromNative() {
					var message;
					try {
						message = JSON.parse(messageJSON);
					} catch(e) {
						//TODO handle the exception
						console.error("Native call H5 Method error,Error in pass in parameter");
						return;
					}

					//Callback function
					var responseCallback;
					if(message.responseId) {
						//It is specified here that when the native execution method is ready to notify h5 to execute the callback, the callback function id is responseId
						responseCallback = responseCallbacks[message.responseId];
						if(!responseCallback) {
							return;
						}
						//Execute the local callback function
						responseCallback(message.responseData);
						delete responseCallbacks[message.responseId];
					} else {
						//Otherwise, it represents the native active execution of h5 local functions
						if(message.callbackId) {
							//First determine whether the local H5 is required to execute the callback function
							//If you need a local function to perform callback notification native, register the callback function locally, and then call native
							//The callback data is passed in after the h5 function is executed
							var callbackResponseId = message.callbackId;
							responseCallback = function(responseData) {
								//The default is to call the functions above the EJS api
								//Then, after the native knows that scheme is called, it will take the initiative to obtain this information
								//Therefore, the native should judge whether the function is successfully executed and receive data
								//At this point, the communication is finished (since h5 will not add a callback to the callback, there is no communication in the next step)
								_doSend({
									handlerName: message.handlerName,
									responseId: callbackResponseId,
									responseData: responseData
								});
							};
						}

						//Get from locally registered functions
						var handler = messageHandlers[message.handlerName];
						if(!handler) {
							//This function is not registered locally
						} else {
							//Execute local functions, pass in data and callbacks as required
							handler(message.data, responseCallback);
						}
					}
				}
			}

		};
		/**
		 * @description JS Before calling the native method, it will send here for processing
		 * @param {JSON} message Details of the method to be called, including method name and parameters
		 * @param {Function} responseCallback Callback after calling method
		 */
		function _doSend(message, responseCallback) {
			if(responseCallback) {
				//Get a unique callbackid
				var callbackId = Util.getCallbackId();
				//Callback functions are added to the collection
				responseCallbacks[callbackId] = responseCallback;
				//Method details add the key identifier of the callback function
				message['callbackId'] = callbackId;
			}
			var uri;
			//In android, you can access it through onJsPrompt or intercepting Url
			var ua = navigator.userAgent;
			if(ua.match(/(iPhone\sOS)\s([\d_]+)/)||ua.match(/(iPad).*OS\s([\d_]+)/)) {
				//In ios, through intercepting the client url to access
				//Because ios can be obtained manually without exposing scheme
				//The details of the method being called are added to the message queue, and will be obtained by the native
				sendMessageQueue.push(message);
				uri = Util.getUri();
			}else{
				//android compatible processing, all parameters are spliced into the url
				uri = Util.getUri(message);
			}
			//Get the url scheme of the trigger method
			//Using iframe to jump to scheme
			messagingIframe.src = uri;
		}

		var Util = {
			getCallbackId: function() {
				//If the port cannot be resolved, it can be replaced by Math.floor ( Math.random () * (1 << 30));
				return 'cb_' + (uniqueId++) + '_' + new Date().getTime();
			},
			//Get url scheme
			//The second parameter is compatible with the practice in android
			//In android, the return value of JS function cannot be obtained by native, so it has to be transmitted through protocol
			getUri: function(message) {
				var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
				if(message) {
					//The callback id exists as a port
					var callbackId, method, params;
					if(message.callbackId) {
						//First: h5 calls native
						callbackId = message.callbackId;
						method = message.handlerName;
						params = message.data;
					} else if(message.responseId) {
						//The second: after native call h5, h5 callback
						//In this case, you need to analyze whether the port passed is a callback defined by it
						callbackId = message.responseId;
						method = message.handlerName;
						params = message.responseData;
					}
					//The parameter is converted to a string
					params = this.getParam(params);
					//uri supplement
					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];
			}
		}

	})();

	//Register a test function
	JSBridge.registerHandler('testH5Func', function(data, callback) {
		alert('The test function received data:' + JSON.stringify(data));
		callback && callback('Test returned data...');
	});
	/*
	 ***************************API********************************************
	 * Open api for external calls
	 * */
	window.jsapi = {};
	/**
	 ***app modular 
	 * Some special operations
	 */
	jsapi.app = {
		/**
		 * @description Test function
		 */
		testNativeFunc: function() {
			//Call a test function
			JSBridge.callHandler('testNativeFunc', {}, function(res) {
				callback && callback(res);
			});
		}
	};
})();				
			

Android implementation

explain

This is the supporting JSBridge implementation code in Android native. The implementation of Android is more complex than JS, including many parts

JSBridge class implementation

The implementation code is as follows

public class JSBridge {
    private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>();
	
	//Native registration API method
    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();
            }
        }
    }
	
	//Get all the registration methods
    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;
    }

	//Calling methods in Hava
	//BaseWebLoader is the webview container of JSBridge (secondary encapsulation)
	//After the method is executed, if it returns, it will be called automatically
    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;
    }
}	
			

The purpose of this class is to natively define some exposed APIs

Callback class implementation

The implementation code is as follows

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));
        //If the activity has been closed, no callback is made
        if (mWebViewRef != null && mWebViewRef.get() != null && !((BaseWebLoader) mWebViewRef.get().getContext()).getActivity().isFinishing()) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mWebViewRef.get().loadUrl(execJs);
                }
            });
        }
    }
}
			

The purpose of this class is to define callback functions in native

Key code implementation of Webview container

The implementation code is as follows

Register api methods

//Define api collection
JSBridge.register("namespace_bridge",BridgeImpl.class);				
			

Code that captures the url scheme and executes the method

public boolean shouldOverrideUrlLoading(WebView view, String url){
	//After reading the url, it is called through callJava analysis
	JSBridge.callJava(BaseWebLoader.this,view,url);
	
	//If false is returned, WebView processes the link url. If true is returned, it means WebView executes the url according to the program
	return true;
}
					
			

The key code is to register function, capture url, execute method and so on

API class implementation

The implementation code is as follows

public class BridgeImpl implements IBridge {
	/**
     * Testing native methods
     */
    public static void testNativeFunc(final BaseWebLoader webLoader, WebView wv, JSONObject param, final Callback callback) {
       //You can get the parameters like this param.optString (key value);
        //Execute in a new thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //Do something of your own
                    JSONObject object = new JSONObject();
                    //Add test information
                    object.put("test", "test");
                    //Execute callback
                    callback.apply(getJSONObject(1, "", object));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
			

This class is the specific implementation of some APIs, and the webview registers these corresponding APIs

iOS implementation

explain

This is the supporting JSBridge implementation code in iOS native. The code in iOS is based on UIWebview, which comes from an open source project on github marcuswestin/WebViewJavascriptBridge

Implementation of WebView javascriptbridgebase

The implementation code is as follows

@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);
        }
    }
}

/*This code is not used in this article
 * In the original project, the js Library of JSBridge is placed in the iOS local sandbox, so it needs to be injected manually
 * But in the example in this article, JSBridge is referenced directly in Html, so no injection is required
- (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
			
			

WebView javascriptbridgebase is the basic support of JSBridge logic code, that is to say, it encapsulates some basic code for JSBridge internal call

Implementation of WebView JavaScript Bridge

The implementation code is as follows

#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];
    }
}
//Capture the url and analyze it
- (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

			
			

WebView JavaScript bridge is the main logic implementation code of JSBridge

Key code implementation of Webview container

The implementation code is as follows

Construction of JSBridge Bridge Bridge

- (void)viewDidLoad {
    
    [super viewDidLoad];
    // Some other operations
    
    // WebViewJavascriptBridge
    // Build a bridge between JS and OC for webView
    [WebViewJavascriptBridge enableLogging];
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wv];
    [self.bridge setWebViewDelegate:self];
    [self registAPIForJS];
}	
			

Register API methods

- (void)registAPIForJS
{
    // Testing native methods
    [self.bridge registerHandler:@"testNativeFunc" handler:^(id data, WVJBResponseCallback responseCallback) {
        //You can get the parameters like this
        NSString *str = data[@"key value"];
       
       //You can do something of your own
    }];
}
			

Code that captures the url scheme and executes the method

//The key is this code
[self.bridge setWebViewDelegate:self];
//Then in WebView javascriptbridge, you can capture the url again with shouldStartLoadWithRequest
			

Posted by benyboi on Thu, 09 Jul 2020 09:13:20 -0700