In-depth Profiling of JSBridge

Overview

Developers who have been involved in hybrid development are familiar with frameworks such as Ionic and PhoneGap. These frameworks cover a web-based app in a Native layer and then call the local library through the JSBridge technology.

Before diving into the details of JSBridge technology, let us briefly explore the traditional implementation process.

Android Environment

Calling JS in Native

Calling JS in Native is relatively simple: you just need to observe the rule of "javascript: method name ('parameter, needed to be converted to string')".

Call method before Android 4.4:

// mWebView = new WebView(this);
mWebView.loadUrl("javascript: method name ('parameter, needed to be converted to string')"); 

//Run in UI thread
 runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mWebView.loadUrl("javascript: method name ('parameter, needed to be converted to string')");
            Toast.makeText(Activity name.this, "call method...", Toast.LENGTH_SHORT).show();
        }
});

Call method for Android 4.4 and newer:

mWebView.evaluateJavascript("javascript: method name ('parameter, needed to be converted to string')", new ValueCallback() {
        @Override
        public void onReceiveValue(String value) {
            //Here the value refers to the returned value of the corresponding JS method
        }
});

Description:

  • Before Android 4.4, Native calls JS method through loadUrl. In this approach, only a JS method is executed, but the returned value of this method cannot be obtained.
  • For Android 4.4 and newer versions, the JS methods are called asynchronously through evaluateJavascript and the returned value is available in onReceiveValue.
  • This method is not suitable for transmission of large amounts of data (the interface approach is recommended for a large amount of data).
  • mWebView.loadUrl("javascript: The mWebView.loadUrl ("javascript:method name ('parameter, needed to be converted to string'")") function needs to be run in the UI thread because mWebView is a UI control.

Calling Native in JS

The call of Native in JS requires the @JavascriptInterface annotation to the WebView. There is a loophole, which will be explained later. To make JS native, you need to set the following attributes for WebView:

WebSettings webSettings = mWebView.getSettings();
 //Android container allows JS scripts
webSettings.setJavaScriptEnabled(true);
//Android container sets the bridge object
mWebView.addJavascriptInterface(getJSBridge(), "JSBridge");

Here we see getJSBridge(). In Native, addJavascriptInterface is used to add an exposed JS bridge object, and then the corresponding API method is declared inside the object.

private Object getJSBridge(){
    Object insertObj = new Object(){
        @JavascriptInterface
        public String foo(){
            return "foo";
        }  

        @JavascriptInterface
        public String foo2(final String param){
            return "foo2:" + param;
        }  

    };
    return insertObj;
}

Calling the Native method in HTML.

//Call method 1
window.JSBridge.foo(); //Returned: 'foo'
//Call method 2
window.JSBridge.foo2('test');//Returned: 'foo2:test'

Description:

  • In Android 4.2 (API17) and newer versions, the exposed API should carry an @JavascriptInterface annotation. Otherwise, the method will not be found.
  • Before API17, addJavascriptInterface has a potential security risk. Hackers can get the Native-registered JS object by decompiling and obtaining sensitive information, and then launching attacks by reflecting Java's built-in static classes on the page.
  • JS calls Native-exposed API and gets the corresponding returned value.

iOS Environment

Calling JS in Native

The method to call JS in Native is relatively simple. Native calls the function that HTML binds to the window through stringByEvaluatingJavaScriptFromString. Note the OC and Swift writing styles.

//Swift
webview.stringByEvaluatingJavaScriptFromString("method name (parameter)")
//OC
[webView stringByEvaluatingJavaScriptFromString:@"method name (parameter);"];

Description:

  • When you call JS methods in Native, you can get the returned value of the JS method.
  • This method is not suitable for transmission of large amounts of data (the interface approach is recommended for a large amount of data).

Calling Native in JS

In Native, the API can be bound to JSContext through the introduction of the official JavaScriptCore library (iOS7 and above) (and then JS can be called in HTML through window.top. *).

Introduce the official library file

#import <JavaScriptCore/JavaScriptCore.h>

Native registers the API function (OC)

-(void)webViewDidFinishLoad:(UIWebView *)webView{
    [self hideProgress];
    [self setJSInterface];
}

-(void)setJSInterface{

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

    // API method registered with a name of "foo"
    context[@"foo"] = ^() {

        //Get the parameter
        NSArray *args = [JSContext currentArguments];
        NSString *title = [NSString stringWithFormat:@"%@",[args objectAtIndex:0]];
        //Do some logic of your own
        //A value is returned: 'foo:'+title
        return [NSString stringWithFormat:@"foo:%@", title];
    };
}

Calling Native methods in JS in HTML

window.top.foo('test');

Description:

  • This approach was not available until iOS 7. Prior to iOS7, JS could not call Native directly. JS can call Native indirectly only through JSBridge.
  • JS can call the exposed API and get the corresponding returned value.
  • Native iOS cannot be called by JS, but APIs can be made open to JS calls by introducing the official third-party "JavaScriptCore".

What is JSBridge?

As its name implies, JSBridge serves as a bridge between JS and Native. In fact, JSBridge, also known as the Hybrid app technology, is a communication method between JS and Native. In JSBridge, Native calls JS only through a fixed bridge object, and vice versa.

JSBridge Implementation Process

The process of JSBridge implementation can be summarized as follows. An H5 page triggers a URL in some way and Native captures the URL for analysis. Native then performs the processing and calls the H5 JSBridge object for transfer and callback.

Since the native WebView/UIWebView controls have already achieved data communication with JS, why do we still need JSBridge?

The following list describes the motivations for using JSBridge:

  • The addJavascriptInterface method has security loopholes in the versions lower than Android 4.2.
  • JS is unable to call Native in versions lower than iOS 7.
  • The URL scheme interaction is a set of existing mature solutions that are perfectly compatible with various versions and with the technology of older versions.

URL Scheme

URL scheme is a link, like the URL, designed to facilitate direct mutual calls between apps. You can open the system application for a system URL scheme. Otherwise, you can try to find whether an app has registered such a scheme and open that corresponding app.
Note: A scheme will be valid only after native app registration is completed.

However, in an actual development scenario, the app does not register the corresponding scheme. Instead, the front-end page triggers the scheme (such as iframe.src) in some way. Native captures the corresponding URL to trigger the event and gets the current triggering URL, and then Native checks if the method is triggered according to the defined protocol.

JSBridge: Technical Implementation

To implement JSBridge, we need to analyze the following steps:

  • Step 1: Design a global bridge object for Native to interact with JS
  • Step 2: How JS calls Native
  • Step 3: How Native knows that the API is called
  • Step 4: URL-parameter and callback format
  • Step 5: How Native calls JS
  • Step 6: Registration and format of the API method in H5

The figure below shows the complete process of JSBridge:

Designing Global Bridge Objects for Interaction Between Native and JS

We stipulate that the communication between JS and Native must be implemented through an H5 global object – JSBridge. The object has the following characteristics:

The object name is "JSBridge", which is an attribute of the global object window in the H5 page. It is elaborated as:

var JSBridge = window.JSBridge || (window.JSBridge = {});

This object has the following methods:

  • registerHandler (String, Function) H5 call. It registers the local JS method, after which Native can make the call through JSBridge. After the call, the method is registered in the local variable messageHandlers.
  • callHandler (String, JSON, Function) H5 call. It calls the Native open API, after which the URL scheme is triggered locally. During the call, the callback ID is stored in the local variable responseCallbacks.
  • _handleMessageFromNative (JSON) Native call. Native calls the method registered on the H5 page, or notifies the H5 page of the execution of the callback method.

Calling Native in JS

After we define the global bridge object, we can use its callHandler method to call the Native API.

Internal implementation process of the callHandler function

The callHandler execution includes the following internal steps:
1.Determine whether a callback function exists. If yes, a callback function ID is generated, and the ID and the corresponding callback is added to the callback function set responseCallbacks.
2.The input data and method name are spliced into a URL scheme through a specific parameter conversion method.

//The URL scheme format
//Basically the useful information is the callbackId, handlerName and data at the end
//Native will analyze the scheme after it captures it
var uri = CUSTOM_PROTOCOL_SCHEME://API_Name:callbackId/handlerName?data

3.A internal hidden which has long been ready iframe is used for triggering the scheme

//Process of creating the hidden iframe
var messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
document.documentElement.appendChild(messagingIframe);
//Trigger the scheme
messagingIframe.src = uri;

Note: Normally it is possible to initiate a network request through window.location.href. However, there is a very serious problem when we change the value of window.location.href several times in succession: the Native layer can only receive the last request, while ignoring all the previous ones. To avoid this problem, we need iframe to initiate network requests at the JS end.

Native Notifying the API to be Called

In our previous step, we successfully triggered the scheme on the H5 page.
Our next step is to explore how Native captures the scheme triggering event.
Android and iOS have their respective handling approaches.

Android

In Android (in WebViewClient), the shouldoverrideurlloading helps to capture the URL scheme triggering event.

public boolean shouldOverrideUrlLoading(WebView view, String url){
    //If false is returned, WebView handles the link URL. If true is returned, WebView executes the URL following the procedures
    return true;
}

iOS

iOS, UIWebView has a unique feature: all the network requests initiated in the UIWebView can be notified to the Native layer through the delegate function. In this way, we can capture the URL scheme triggering event in WebView (the principle is to use shouldStartLoadWithRequest).

- (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType {
    NSURL *url = [request URL];

    NSString *requestString = [[request URL] absoluteString];
    //Get the URL scheme for self-processing

Analyzing URL-parameter and Callback Format

In previous steps, Native has received a JS call method. In the next step, Native should parse the data according to the defined data formats. Native can extract the callback parameter ID, API name, and parameters following this format after receiving the URL, and then follow the steps below.
1. Search for the corresponding API method locally according to API name, and record the callback function ID after the method is executed.
2. Convert the extracted parameters according to the defined parameters.
3. Native executes the corresponding API function method locally.
4. After function execution, find the corresponding callback function ID of this API call, and then assemble the information with the parameter information to be passed into a JSON format parameter.
5. Notify the H5 page for callback via JSBridge.

Calling JS in Native

In this step, Native calls H5 JS methods through JSBridge or notify H5 for callback. The messageJSON data format in it has two different types.

JSBridge._handleMessageFromNative(messageJSON);

Native notifies H5 page for callback:
As per the data format, Native notifies H5 about the callback JSON format.

Native takes the initiative to call the H5 method:
When Native takes the initiative to call H5 methods, the data format is: {handlerName: the API name, data: data, callbackId: the callback ID}:

  • handlerName String type: the open API name to be called in H5
  • data JSON type: the data to be passed. Its format is fixed to be JSON (because we fix the format of the first parameter received by methods registered in H5 to JSON, and the second to a callback function)
  • callbackId String type: the callback function ID generated by Native. After H5 execution, the URL scheme is used to notify Native about the successful execution of the API and to pass the parameter.

Registration and Format of the API Method in H5

Previously we mentioned that Native takes the initiative to call the API methods registered in H5.
Let us now study, how we can register API methods in H5 for the Native to call.

JSBridge.registerHandler('testH5Func',function(data,callback){
    alert('Test data received by the function:'+JSON.stringify(data));
    callback&&callback('Test the postback data...');
});

As indicated in the code above, the first data is the data passed by the Native. The second callback is encapsulated once internally. After the callback is executed, it triggers the URL scheme and notifies the Native to get the callback information.

Improving the JSBridge Scheme

The diagram below shows a JSBridge object.

The following diagram illustrates the full process of JSBridge implementation.

JSBridge in iOS and Android

In an actual development, how can we develop a unified scheme for different situations in both Android and iOS?

The JSBridge mentioned above is based on the URL scheme. However, if you do not plan to support versions lower than Android 4.2 and iOS 7, you must consider another scheme.

  • Native calls of JS methods remain unchanged
  • JS calls of Native are no longer through triggering the URL scheme, but through the built-in interaction. Specifically, in Android, Native opens a unified API for JS to call through addJavascriptInterface. Then the URL scheme triggering step is changed to call of the API while the other steps remain the same.

In iOS, Native registers a unified API through methods in JavaScriptCore, and the other steps are the same with those in Android.

Conclusion

Javascript Bridge is a unified messaging system that enables easy building of cross-language services that allow you to share data and real-time updates among your servers and clients. This article explores in detail the profiling of JSBridge and its implementation process in both iOS and Android respectively.

时间: 2024-11-08 17:35:17

In-depth Profiling of JSBridge的相关文章

Flash AS学习:讲解Movie Clip Depth(深度)的问题

问题 大家对duplicateMovieClip()应该不会陌生吧,它用来复制MC.duplicateMovieClip()有个参数----Depth,它就是用来控制新复制出来的MC的深度,数字越大,代表越顶层.   举个例子:      舞台上有1个MC1,在帧上写           mc1.duplicateMovieClip("mc2",2);           mc1.duplicateMovieClip("mc3",20);           再加上

常用Java Profiling工具的分析与比较

在 Java 程序的开发过程中,不可避免地会遇到内存使用.性能瓶颈等问题.Java Profiler 工具能帮助开发人员快速.有效地定位这些问题,因此成为了 Java 开发过程中的一个重要工具.目前市场上的 Java Profiler 工具种类繁多,本文将对目前比较常见的几种工具进行简要介绍,并从功能.性能等角度作比较,从而帮助 Java 程序员选择合适的 Java Profiler 工具. 本文主要分为三个部分:第一部分简要介绍 Java Profiler 工具的原理:第二部分对目前常见的 J

云上Java System Profiling与Debugging——蚂蚁金服观察与实践

从1995年Java1.0beta发布到现在,整整过去了20年.Java的发明源于嵌入式领域,不过后来Java的发展,出乎意料地在企业级应用领域占据了几乎统治的地位.阿里巴巴以及支付宝(就是后来的蚂蚁金服),绝大部分的业务代码都是Java编写的.在Java20岁生日这年,我们用这篇文章记录蚂蚁金服内部,在金融云环境下Java系统的Profiling和Debugging经验与实践,与大家分享交流,有非常特别的意义.希望读者能够从中借鉴到一些解决问题的思路. 线上Profiling/Debuggin

PostgreSQL 源码性能诊断(perf profiling)指南

标签 PostgreSQL , Linux , perf , 性能诊断 , stap , systemtap , strace , dtrace , dwarf , profiler , perf_events , probe , dynamic probe , tracepoint 背景 数据库的性能优化是一个非常经典的话题,数据库的优化手段以及优化的角度也各不相同. 例如,可以从OS内核.网络.块设备.编译器.文件系统.SQL.数据库参数.业务逻辑.源码等各个方面去进行优化. 但是如果在优化

为自己搭建一个鹊桥 -- Native Page与Web View之间的JSBridge实现方式

原文:为自己搭建一个鹊桥 -- Native Page与Web View之间的JSBridge实现方式 说起JSBridge,大家最熟悉的应该就是微信的WeixinJSBridge,通过它各个公众页面可以调用后台方法和微信进行交互,为用户提供相关功能.我们就来说说UWP下怎么样实现我们自己的JSBridge. 在win10之前,如果需要实现JSBridge,我们大概有两种方法: 1. window.external.notify 做过webview的小伙伴肯定都熟悉,html页面可以通过wind

LeetCode 104 Maximum Depth of Binary Tree(二叉树的最大深度)

翻译 给定一个二叉树,找出它的最大深度. 最大深度是指的从根节点一直到最远的叶节点中所有的节点数目. 原文 Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. 代码 /** * Definition for a binary tre

visual studio 2010- Visual Studio Profiling Control Driver 的作用是什么?

问题描述 Visual Studio Profiling Control Driver 的作用是什么? 本人大一,IP课老师让下载这个软件,我们这学期讲的是C ,我用Win8.1 的系统,但是他却阻止 Visual Studio Profiling Control Driver的运行,不知道是否会对软件产生影响,先谢谢了!!!我下载的版本是VS2010 解决方案 建议使用2013,2010比较老了,和Win8.1兼容性不太好.但是如果你只是学校里面学点简单的C语言,显然这东西应该没关系的.

设置-nutch TopN 50万 depth 10 获取到6万多数据

问题描述 nutch TopN 50万 depth 10 获取到6万多数据 我用nutch1.9搜索阿里巴巴的网站,使用的是bin/crawl 脚本执行的,topN设置的50万,爬行深度设置的10,url过滤只允许阿里巴巴的网站,但是实际搜出来的结果却只有6万多,又人知道大概是什么原因吗困扰了好几天了.....求教 解决方案 网站发现你爬虫,ban了你的请求 解决方案二: 这个没很好办法,模拟浏览器,多换IP,降低频率.

JSBridge深度剖析

概述 做过混合开发的人都知道Ionic和PhoneGap之类的框架,这些框架在web基础上包装一层Native,然后通过Bridge技术的js调用本地的库. 在讲JSBridge技术之前,我们来看一下传统的实现方式. Android端 Native调JS native调用js比较简单,只要遵循:"javascript: 方法名('参数,需要转为字符串')"的规则即可. 在4.4之前,调用的方式: // mWebView = new WebView(this); mWebView.loa