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.
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:
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:
@JavascriptInterface
annotation. Otherwise, the method will not be found.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.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:
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:
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.
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:
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.
To implement JSBridge, we need to analyze the following steps:
The figure below shows the complete process of JSBridge:
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:
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.
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.
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, 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
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.
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}:
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.
The diagram below shows a JSBridge object.
The following diagram illustrates the full process of JSBridge implementation.
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.
In iOS, Native registers a unified API through methods in JavaScriptCore, and the other steps are the same with those in Android.
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.
2,599 posts | 762 followers
FollowAlibaba Cloud Community - October 15, 2024
Alibaba Cloud Native - April 16, 2024
Alibaba Cloud Native Community - May 9, 2023
Alibaba Developer - August 9, 2021
Alibaba F(x) Team - June 20, 2022
Rupal_Click2Cloud - December 15, 2023
2,599 posts | 762 followers
FollowHighly reliable and secure deployment solutions for enterprises to fully experience the unique benefits of the hybrid cloud
Learn MoreA cost-effective, efficient and easy-to-manage hybrid cloud storage solution.
Learn MoreA low-code development platform to make work easier
Learn MoreHelp enterprises build high-quality, stable mobile apps
Learn MoreMore Posts by Alibaba Clouder