1. Basic concepts of JSBridge

JSBridge mainly provides an interface for JavaScript to invoke Native functions, so that Native functions (such as address location and camera) can be easily used in the front end of mixed development. Its core is to build the channel of message communication between Native and non-native, and this communication channel is bidirectional.

So there are four elements to build this two-way channel:

1. Communication triggering behavior:

H5 behavior that can be natively listened on and captured. It includes console.log, alert, confirm, Prompt, location.href, etc. In order not to affect the flow of the original H5 page, it generally creates an IFrame to initiate the pseudo-request protocol, and deletes the IFrame after the call to realize the whole process of the call.

2. Communication Protocol:

A user-defined pseudo-protocol is usually defined in the following format: – Protocol name :// Interface path? Parameter 1=XXX& parameter 2=XXX& parameter 3=XXX#callback

  • Among them:
  • A. Protocol name: the protocol name defined by app, used for H5 triggering behavior monitoring capture, such as jsbridge://;
  • B. Interface path: the path of specific native capabilities, which varies with different native capabilities;
  • C, parameter 1=XXX& parameter 2=XXX& parameter 3=XXX#callback: According to the communication protocol specification, H5 can be provided with different call addresses for different native capabilities, such as: jsbridge://method? a=2&b=3#h5MethodTag
3. Communication behavior — call:

According to the content sent by H5, the native terminal will route to the specific processing method after parsing and matching, and execute the native capability logic.

4. Communication behavior — Callback:

Native captures the method name of the JS callback function based on the content passed by H5. After the execution of the native logic, the result is brought to the callback function and the JS callback function is executed. After the third “call” is executed, iOS calls the JS callback function H5MethodTag.

Graph LR front end -- Communication trigger --> native: Complete function calls based on communication protocol -- callback --> front end

Ii. Implementation premise of JSBridge

JavaScript runs in a separate JS Context(e.g., Webkit engine WKWebView and UIWebView, JSCore), Different platforms (iOS, Android, etc.) use different Context to provide corresponding communication methods with native, we need to unified encapsulation and implementation of these methods.

Iii. Concrete implementation of JSBridge

Among the four elements, the second communication protocol and the third native specific processing method are the parts that need to be enriched and expanded. The first communication trigger and the fourth native callback are the skeleton of the whole JSBridge. Let’s explain these two points.

1. Front-end call

There are native injection apis and intercepting URL schemes.

1.1 Native injection API

The main principle of the Native injection API method is to inject objects or methods into the JavaScript Context (window) through the interface provided by WebView, so that the corresponding Native code logic can be directly executed when JavaScript is called. To achieve the purpose of JavaScript calling Native. An example of UIWebView for iOS is as follows:

// Native method registration
JSContext *context = [uiWebView valueForKeyPath:@"DocumentView. WebView. MainFrame. JavaScriptContext"]; PostBridgeMessage context[@"postBridgeMessage"] = ^ (NSArray < > NSArray * * calls) {/ / Native logic... }; Window.postbridgemessage (message);Copy the code

An example of WKWebView for iOS is as follows:

// Native method registration
@interface WKWebVIewVC ()<WKScriptMessageHandler> 
@implementation WKWebVIewVC 
- (void)viewDidLoad { 
    [super viewDidLoad];
    WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
    configuration.userContentController = [[WKUserContentController alloc] init];
    WKUserContentController *userCC = configuration.userContentController; 
    / / into the object, the front-end to call its methods, Native can catch [userCC addScriptMessageHandler: self name: @ "nativeBridge"];
    WKWebView wkWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
    // TODO displays the WebView
 } 
 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { 
 if ([message.name isEqualToString:@"nativeBridge"]) { 
     NSLog(@"Data passed from the front end %@:",message.body); 
     / / Native logic. }}// Front-end invocation
window.webkit.messageHandlers.nativeBridge.postMessage(message); 
Copy the code

Reference documentation blog.csdn.net/yuzhengfei7…

1.2 URL Interception SCHEME

The main flow is as follows: The Web sends a URL Scheme request in a certain way (for example, iframe.src). Then the Native intercepts the request and performs related operations according to the URL Scheme (including the parameters). In the process of using this method, there are certain drawbacks: Sending a URL SCHEME using iframe.src may cause a hidden URL length problem. Creating a request takes a certain amount of time, which is longer than injecting the API to invoke the same function.

// listen for URL changes natively
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { 
    NSURL *URL = request.URL;
    NSString *scheme = [URL scheme];
    if ([scheme isEqualToString:@"Agreement Name"]) {
        [self handleCustomAction:URL];
        return NO; 
    } 
  return YES; 
} 

// Front-end invocation
function loadURL(url) { 
    var iFrame; iFrame = document.createElement("iframe");
    iFrame.setAttribute("src", url); iFrame.setAttribute("style"."display:none;"); 
    iFrame.setAttribute("height"."0px"); iFrame.setAttribute("width"."0px"); 
    iFrame.setAttribute("frameborder"."0");
    document.body.appendChild(iFrame); 
    // This iFrame is useless after the request is made, so remove it from the DOM
    iFrame.parentNode.removeChild(iFrame); iFrame = null; 
} 
loadURL("esales://ZTHShowPicker"); 
Copy the code

Reference documentation www.cnblogs.com/dianming/p/…

2.Native callbacks to JavaScript

Compared with JavaScript calling Native, Native JavaScript calling is relatively simple, and it can execute the spliced JavaScript code directly. Methods in JavaScript are called externally, so JavaScript methods must be on the global window.

For an iOS UIWebView, the following is an example:

result = [uiWebview stringByEvaluatingJavaScriptFromString:javaScriptString]; * javaScriptString is a string of JavaScript codeCopy the code

An example of WKWebView for iOS is as follows:

[wkWebView evaluateJavaScript:javaScriptString completionHandler:completionHandler]; 
Copy the code

4. JSBridge protocol analysis

Overall process: It is also very simple to call Native logic with JSBridge JavaScript at the Native end. The main code logic is as follows: If the JavaScript message is received => parse the parameters and get bridgeName, data and callbackId => find the function method according to bridgeName. If data is used, the return value and callbackId are sent back to the front end. Native calls to JavaScript are just as simple. It automatically generates a unique ResponseId, stores the handle, and sends it to the front end along with data.

Graph LR javascript --bridgeName://data#callbackId--> native javascript

Extension: Intercepts JSBridge front-end references to URL schemes

Therefore, the whole JSBridge is divided into two parts, the front end and the native end. The native end method has no objection but to implement the code in the native end, so the front end code can be introduced in two ways.

1. Injection is performed by Native terminal

The injection method is similar to Native JavaScript, which directly executes all the code of the bridge.

Its advantages are:

The bridge version is easy to align with Native, and Native does not need to be compatible with different versions of JSBridge.

Its disadvantages are:

As the injection timing is uncertain, the mechanism of retry after injection failure should be implemented to ensure the success rate of injection. At the same time, the JavaScript side should give priority to determine whether JSBridge has been successfully injected when invoking the interface.

2. Referenced by the JavaScript side

Its advantages are:

The JavaScript side can determine the existence of JSBridge by calling it directly.

Its disadvantages are:

If the Bridge implementation changes, JSBridge should be compatible with multiple versions of Native Bridge or Native Bridge should be compatible with multiple versions of JSBridge.