What is a jsbridge

Jsbridge is a bridge between the client and H5, through which we can capture some of the native capabilities, while the client can also use some of the methods we provide. Realize two-way communication.

Jsbridge principle

The client can inject some javascript context into the WebView, which can be interpreted as mounting some methods on the window object. H5 can then retrieve this method from the specific object, and vice versa. Js mounts some methods on the Window object. The client can also call some js methods.

The specific implementation

Option 1: Inject the API

IOS UIWebView
JSContext *context = [uiWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

context[@"postBridgeMessage"] = ^(NSArray<NSArray *> *calls) {
    // Native 逻辑
};
Copy the code

H5 is called with window.postbridgemessage (message)

IOS WKWebView
@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]; } - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {if ([message.name isEqualToString:@"nativeBridge"]) {
        NSLog(@"Data passed from the front end %@:",message.body); // Native logic}}Copy the code

H5 use Windows. Its. MessageHandlers. NativeBridge. PostMessage (message) method calls.

Let’s look at the H5 side implementation

Suppose we need a getUserInfo method for H5 to get information about the current APP logged-in user.

So we can js like this:

import registerCallback from '.. /registerCallback';

export default function getUserInfo() {
  returnnew Promise((resolve, reject) => { try { window.webkit.messageHandlers.getUserInfo.postMessage({ callback: registerCallback(resolve), }); } catch (e) { reject(e); }}); }Copy the code

We define a getUserInfo method, this approach would be to call the window. Its. MessageHandlers. GetUserInfo. PostMessage method, this is the way to the client to write, and then pass in a callback methods. The client will give us the information we need through the callback method.

We can also look at what registerCallback is:

window.knCallbacks = {};

function makeRandomId(func) {
  return `${func.name || 'anonymous'}_${Date.now()}`;
}

export default function registerCallback(callback, keepAlive) {
  if(! callback) {return null;
  }

  const callbackId = makeRandomId(callback);
  window.knCallbacks[callbackId] = (data) => {
    let result;
    if (typeof data === 'object') {
      result = data;
    } else if (typeof data === 'string') {
      try {
        result = JSON.parse(data);
      } catch (e) {
        result = data;
      }
    }
    callback(result);
    if (!keepAlive) {
      delete window.knCallbacks[callbackId];
    }
  };

  return `knCallbacks.${callbackId}`;
}
Copy the code

In this method, we will generate a random function name for promise’s resolve method to avoid the problem of overwriting a window function with the same name. We then rewrite a function based on the generated function name and pass this function back to the external callback, which is passed to the client. The data in this case is the data passed by the client through the callback call. Here we do some processing on the data and give it to callback, which is the Promise’s resolve.

Android

Client implementation:

publicclassJavaScriptInterfaceDemoActivityextendsActivity{
  private WebView Wv;

  @Override
  publicvoidonCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    Wv = (WebView)findViewById(R.id.webView);
    final JavaScriptInterface myJavaScriptInterface = new JavaScriptInterface(this);
    Wv.getSettings().setJavaScriptEnabled(true);
    Wv.addJavascriptInterface(myJavaScriptInterface, "knJSBridge"); / / TODO shows publicclassJavaScriptInterface WebView} {the Context mContext; JavaScriptInterface(Context c) { mContext = c; } publicvoidpostMessage(String webMessage){// Native logic}}Copy the code

H5 implementation

import registerCallback from '.. /registerCallback';

export default function getUserInfo() {
  returnnew Promise((resolve, reject) => { try { window.knJSBridge.getUserInfo(JSON.stringify({ callback: registerCallback(resolve), })); } catch (e) { reject(e); }}); }Copy the code

Here we can actually see that knJSBridge is actually a field that we have agreed with the client. We can then call the client’s methods via window.knjsbridge. RegisterCallback is the same as IOS.

According to the above we H5 here can finally achieve such a structure of jsBridge

jsbridge
  - index.js
  - ios/
    - index.js
    - getUserInfo.js
  - android/
    - index.js
    - getUserInfo.js
Copy the code
// jsbridge/index.js
import iosBridge from './iOs';
import androidBridge from './android';

export default class Bridge {
  constructor() {
    super();
    if (isAndroid) {
      this.jsbridge = androidBridge;
    } else{ this.jsbridge = iosBridge; } } getUserInfo = (... args) => this.jsbridge.getUserInfo(... args); } // ios/index.js import getUserInfo from'./getUserInfo';

export {
  getUserInfo
}

// ios/getUserInfo.js
import registerCallback from '.. /registerCallback';

export default function getUserInfo() {
  returnnew Promise((resolve, reject) => { try { window.webkit.messageHandlers.getUserInfo.postMessage({ callback: registerCallback(resolve), }); } catch (e) { reject(e); }}); } // android/index.js import getUserInfo from'./getUserInfo';

export {
  getUserInfo
}

// android/getUserInfo.js
import registerCallback from '.. /registerCallback';

export default function getUserInfo() {
  returnnew Promise((resolve, reject) => { try { window.webkit.messageHandlers.getUserInfo.postMessage({ callback: registerCallback(resolve), }); } catch (e) { reject(e); }}); }Copy the code

Scheme 2: URL interception

In this scenario, H5 constructs an IFrame, initiates a request by setting SRC to the iframe, and then the client intercepts the request.

The URL is typically set to a special string, such as https://__bridge__, followed by the data we need to pass, including the name of a callback function, which the client intercepts and passes back.

Front-end Collector (wechat id: FeDaily)

Collect excellent front-end technology information from the whole network, share with you, and grow together.