What is the Hybird App

The word ‘hybrid’ means’ mixed ‘; As the name suggests, a hybrid app means a hybrid application. In other words, it refers to an App between Web App and Native App.

Generally speaking, Hybird APP is a hybrid mode application formed after Native App embedded H5 pages into the application through Webview component (a browser kernel), which overcomes the natural deficiency of having to reissue the original application after modification.

The classification of Hybird

  • Multi-view hybrid

    The main feature of this mode is to take webView as a view component in Native, which can run and display independently when needed. That is to say, the main body is Native, and Web technology only plays a supplementary role

    The model was almost native, didn’t make it any easier, and by 16 years almost no one was using it

  • Single View hybrid

    This mode is in the same view, including Native View and Webview at the same time (the relationship between each other is stacked), for example, some applications will use H5 to load Baidu map as the main content of the whole page, and then cover some Native view on the webview, such as search

    This mode has a better experience after the completion of development, but the development cost is high, and it is generally suitable for some native personnel to use

  • Web body type

    This mode is Hybrid development in the traditional sense, and many Hybrid frameworks are based on this mode, such as PhoneGap,AppCan,Html5+ and so on

    One of the biggest features of this model is that the Hybrid framework already provides various apis, packaging tools, debugging tools, and then the actual development does not use any native technology. It is actually written in H5 and JS, and then JS can call the native API to implement some extended functions. Often the program from the entry page, to every function is h5 and JS completed

    In theory, the model should be the best one model (because H5 and js to write the most fast, can call native API function is perfect enough), but because of the limitation of some webview itself led to the pattern on the performance loss, including the lack of some memory control, so lead to a much experience less than the original

    Of course, this model should be optimal if it solves the problem of poor experience (e.g., the experience is good on iOS due to good H5 support).

  • Multi-agent coexistence type (flexible type)

    The existence of this mode is to solve the deficiency of web main type. One of the biggest characteristics of this mode is that native development and H5 development coexist, that is to say, for some page modules with high performance requirements, use native to complete, and for some general type modules, use H5 and JS to complete

    This mode generally has cross-platform characteristics, and user experience, high performance, as good as native, but there is a big limitation is that the use of this mode requires a certain technical premise

    That is to say, this pattern is different from the web body type can directly use third-party frameworks, this model is usually some technical support for its own implementation, including the H5 and native communication, native API provides, some processing all of the containers are done by native people, so, the use of this technology is the premise of must have professional native personnel (including Androi D,iOS) and business developers (native developers responsible for functions, front-end solutions for simple universal H5 functions)

    Of course, if there are no technical problems, the App experience developed by using this scheme is very good, and the performance is not inferior to the original, so it is a very good scheme

Native and H5 interaction principle

Since it is a hybrid mode, how Native interacts with H5 is a core issue. There are two modes of interaction

  • Android, iOS native and H5 basic communication mechanism
  • Communication mechanism based on Jsbridge

Android, IOS native and H5 basic communication mechanism

As H5 is attached to Android and iOS Webview components, Android and iOS can directly call JavaScript code through Webview.

Android native and H5 basic communication mechanism
Before version 4.4, this method was version-free, but could not get the return value of a function. The prompt method should be used for compatibility, so that the H5 end can send data through Prompt, and the client end can intercept and obtain data.
mWebView = new WebView(this);		
mWebView.loadUrl("Javascript: method name (' parameter, must be converted to string ')"); 
runOnUiThread(new Runnable() {  
        @Override  
        public void run(a) {
            // The function needs to run in the UI thread because mWebView is a UI control and blocks the UI thread.
            mWebView.loadUrl("Javascript: method name (' parameter, must be converted to string ')"); Toast. MakeText (name of the Activity.this."Call method...", Toast.LENGTH_SHORT).show(); }});After version 4.4, execute the JS code asynchronously and get the return value
mWebView.evaluateJavascript("Javascript: method name (' parameter, must be converted to string ')".new ValueCallback() {
        @Override
        public void onReceiveValue(String value) {
    		// Where value is the return value of the corresponding JS method}});Copy the code
Basic communication mechanism between IOS native and H5

StringByEvaluatingJavaScriptFromString JS function return values can be obtained, but the method must be binding on the top of the page on the window object, such as: the window. The top. The foo

// Swift 
webview.stringByEvaluatingJavaScriptFromString("Method name (parameter)")
// OC 
[webView stringByEvaluatingJavaScriptFromString:@"Method name (parameter);"];
Copy the code
JavaScript call IOS

The introduction of the official library file # import < JavaScriptCore/JavaScriptCore. H >, registered Native API

// Set up some js interfaces after the webView is loaded
-(void)webViewDidFinishLoad:(UIWebView *)webView{
    [self hideProgress];
    [self setJSInterface];
}

-(void)setJSInterface{
    
    JSContext *context =[_wv valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    // Register an API method named foo
    context[@"foo"] = ^() {
    	
    	// Get parameters
        NSArray *args = [JSContext currentArguments];
        NSString *title = [NSString stringWithFormat:@"% @",[args objectAtIndex:0]].// Do some logic of its own
        // Returns a value of 'foo:'+title
        return [NSString stringWithFormat:@"foo:%@", title];
    };
}	
Copy the code

JavaScript calls native code

// Call the method with top to ensure that the call reaches the top level, since the iframe needs top to get the top level
window.top.foo('test'); / / return: 'foo: test'
Copy the code

Native uses the official JavaScriptCore library (available in iOS7) to bind the API to JSContext, so that Javascript can call the API registered by iOS (windot.top.*).

Communication mechanism based on Jsbridge

First, the Web initiates network requests in the following ways

  • location.href
  • A label
  • ajax
  • iframe

Native does not intercept ajax requests; When the location. Href jumps for several times, the WebView will directly filter out the subsequent jump request. Therefore, in H5, iframe is generally used if a jump link is needed and accurate perception of the client is required.

Based on the above, in Jsbridge, the interaction principle between H5 and Native mainly relies on intercepting page actions to make corresponding actions. Native can intercept from two points:

  1. WebView URL Scheme jump interception
  2. Prompt /console/alert interception in WebView: Prompt is usually used because it is used less frequently in the front end and is less likely to cause conflicts

The following mainly describes the Webview URL Scheme interception.

What is a URL Scheme

A URL Scheme is a URL – like link (spread-in parameters) designed to facilitate calls between apps. You can open it with the OpenURI of the system, and then the system will judge. If it is the URL scheme of the system, open the system application; otherwise, check whether there is any App registered with such scheme and open the corresponding App.

Some scheme arrangements on the net:

  • The iOS URL Scheme is being collected continuously
  • Common URL Scheme
H5 is based on the principle of scheme and Native interaction

As can be seen from the figure above, the interaction process between H5 and Native is mainly completed by triggering a scheme. To trigger this action, a middleware is required to connect H5 and Native, which is JsBridge. With JsBridge, you need to consider the following questions:

  1. What functionality does JsBridge need
  2. How does Javascript interact with Native over JsBridge
  3. How does Native interact with H5 based on JsBridge

See below

Where does JsBridge come from and what does it do

JsBridge is an object mounted on the window and injected by Native; The fields in it are all convention because they are used in Native JS initializations, such as WVJBCallbacks. The initialization code is as follows

; (function() {
    // If already initialized, returns.
    if (window.WebViewJavascriptBridge) {
        return;
    }
    if (!window.onerror) {
        window.onerror = function(msg, url, line) {
            console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":"+ line); }}// Initialize some attributes.
    var messagingIframe;
    // Used to store a list of messages
    var sendMessageQueue = [];
    // Used to store messages
    var messageHandlers = {};
    // Use the following combination of protocols to determine if it is a specific message, and then block.
    var CUSTOM_PROTOCOL_SCHEME = 'https';
    var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
    //oc calls the js callback
    var responseCallbacks = {};
    // The id of the message
    var uniqueId = 1;
    // Whether to set message timeout
    var dispatchMessagesWithTimeoutSafety = true;
    // The Web side registers a message method
    function registerHandler(handlerName, handler) {
        messageHandlers[handlerName] = handler;
    }
    // The web side invokes an OC registered message
    function callHandler(handlerName, data, responseCallback) {
        if (arguments.length == 2 && typeof data == 'function') {
            responseCallback = data;
            data = null;
        }
        _doSend({ handlerName: handlerName, data: data }, responseCallback);
    }
    function disableJavscriptAlertBoxSafetyTimeout() {
        dispatchMessagesWithTimeoutSafety = false;
    }
        // Return the message as a JSON string
    function _fetchQueue() {
        var messageQueueString = JSON.stringify(sendMessageQueue);
        sendMessageQueue = [];
        return messageQueueString;
    }
    //OC calls the entry method of JS
    function _handleMessageFromObjC(messageJSON) {
        _dispatchMessageFromObjC(messageJSON);
    }

    // to initialize the bridge object, OC can call various methods in JS via WebViewJavascriptBridge.
    window.WebViewJavascriptBridge = {
        registerHandler: registerHandler,
        callHandler: callHandler,
        disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
        _fetchQueue: _fetchQueue,
        _handleMessageFromObjC: _handleMessageFromObjC
    };


    // Process the message returned from OC.
    function _dispatchMessageFromObjC(messageJSON) {
        if (dispatchMessagesWithTimeoutSafety) {
            setTimeout(_doDispatchMessageFromObjC);
        } else {
            _doDispatchMessageFromObjC();
        }

        function _doDispatchMessageFromObjC() {
            var message = JSON.parse(messageJSON);
            var messageHandler;
            var responseCallback;
            / / callback
            if (message.responseId) {
                responseCallback = responseCallbacks[message.responseId];
                if(! responseCallback) {return;
                }
                responseCallback(message.responseData);
                delete responseCallbacks[message.responseId];
            } else {// active call
                if (message.callbackId) {
                    var callbackResponseId = message.callbackId;
                    responseCallback = function(responseData) {
                        _doSend({ handlerName: message.handlerName, responseId: callbackResponseId, responseData: responseData });
                    };
                }
                // Get the JS registered function
                var handler = messageHandlers[message.handlerName];
                if(! handler) {console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
                } else {
                    // Call the corresponding function in JShandler(message.data, responseCallback); }}}}// Send the message from JS to OC to perform the specific send operation.
    function _doSend(message, responseCallback) {
        if (responseCallback) {
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            // The callback ID that stores the message
            responseCallbacks[callbackId] = responseCallback;
            // Send the corresponding callback ID with the message for later use after the message returns.
            message['callbackId'] = callbackId;
        }
        // Put the message into the message list
        sendMessageQueue.push(message);
        // The following statement will initiate a JS call to OC
        // Make the webView perform the jump operation so that the
        //webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction DecisionHandler :(void (^)(WKNavigationActionPolicy))decisionHandler intercepts the message sent by JS to OC
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + ': / /' + QUEUE_HAS_MESSAGE;
    }


    messagingIframe = document.createElement('iframe');
    messagingIframe.style.display = 'none';
    //messagingIframe.body.style.backgroundColor="#0000ff";
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + ': / /' + QUEUE_HAS_MESSAGE;
    document.documentElement.appendChild(messagingIframe);


    / / registered _disableJavascriptAlertBoxSafetyTimeout method, OC can shut down the callback timeout, default is open.
    registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
    // Execute the _callWVJBCallbacks method
    setTimeout(_callWVJBCallbacks, 0);

    // Initializes the method registered in the WEB. This method registers the Hander from the WEB into the Bridge.
    // The following code executes the WEB callback function.
    function _callWVJBCallbacks() {
        var callbacks = window.WVJBCallbacks;
        delete window.WVJBCallbacks;
        for (var i = 0; i < callbacks.length; i++) { callbacks[i](WebViewJavascriptBridge); }}}) ();Copy the code

JsBridge acted as a middleman. Therefore, it needs to provide H5 with methods to register the callback function

registerHandler(cmd, callback)

And the interface to call Native methods

callHandler(cmd, ... params, callback)

CMD is the identifier of the unified agreement between Native developers and Web developers, which can be imagined as a function name. The JsBridge object has the following methods and properties

How does Javascript interact with Native over JsBridge

H5 interacts with Native by calling JsBridge. CallHandler. After calling this method, the JsBridge content takes the following steps:

  1. Determine if there is a callback function, and if so, generate a callback id and add the ID and corresponding callback to the callback collection responseCallbacks

  2. By a specific parameter conversion method, the incoming data, method name, joining together into a url scheme (format, such as: CUSTOM_PROTOCOL_SCHEME: / / API_Name: callbackId/handlerName? data)

  3. Scheme is triggered using a hidden IFrame that is already created internally

    // Create a hidden iframe procedure
    var messagingIframe = document.createElement('iframe');
    messagingIframe.style.display = 'none';
    document.documentElement.appendChild(messagingIframe);
    
    / / trigger scheme
    messagingIframe.src = uri;
    					
    Copy the code
How does Native interact with H5 based on JsBridge

After capturing the URL scheme, Native parses the URL according to its format to obtain the required callback function ID, parameters and other information. Perform the following steps:

  1. According to the API name, find the corresponding API method locally, and record the callback function ID after the method is executed
  2. According to the extracted parameters, according to the defined parameters for conversion (if it is JSON format need to be manually converted, if it is String format can be directly used)
  3. The corresponding API function methods are executed natively
  4. After the function is executed, find the callback function ID corresponding to this API call, and then assemble a JSON format parameter together with the parameter information to be passed. The JSON format of the callback is:{responseId: callback id,responseData: callback data}
    • ResponseId

      : id of the H5 page corresponding to the callback function to execute, generated when the URL scheme was generated in H5
    • ResponseData

      : callback data that Native needs to pass to H5

reference

  • What is a Hybrid APP
  • JSBridge: From principle to Use
  • Principles of JSBridge
  • Pick up a Hybrid framework from scratch (I) – Start by selecting JS communication schemes
  • WebViewJavascriptBridge principle parsing