JSBridge概念
JSBridge顾名思义就是是一座用JavaScript搭建起来的桥,一端是web,一端是native。可以实现web 与Native之间相互调用。
实现原理
Native>>Web
-
Android
webview.loadUrl(“JavaScript:function()”);
-
IOS
webview.stringByEvaluatingJavaScriptFromString("JavaScript:function()");
Web>>Native
Javascript调用Native,并没有现成的API可以直接拿来用,而是需要间接地通过一些方法来实现
IOS
UIWebView有个特性:在UIWebView内发起的所有网络请求,都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView内发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&m2=value2,于是在UIWebView的delegate函数中,我们只要发现是jsbridge://开头的地址,就不进行内容的加载,转而执行相应的调用逻辑
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
print("shouldStartLoadWithRequest")
let url = request.URL
let scheme = url?.scheme
let method = url?.host
let query = url?.query
if url != nil && scheme == "jsbridge" {
print("scheme == \(scheme)")
print("method == \(method)")
print("query == \(query)")
switch method! {
case "getData":
self.getData()
case "putData":
self.putData()
case "gotoWebview":
self.gotoWebview()
default:
print("default")
}
return false
} else {
return true
}
}
Android
在Android开发中,能实现Js调用Java,有4种方法:
- JavascriptInterface
- WebViewClient.shouldOverrideUrlLoading();
- WebChromeClient.onConsoleMessage();
- WebChromeClient.onJsPrompt()
JavascriptInterface
这是Android提供的Js与Native通信的官方解决方案。
首先Java代码要实现这么一个类,它的作用是提供给Js调用。
public class JavascriptInterface {
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
}
}
然后把这个类添加到WebView的JavascriptInterface中。webView.addJavascriptInterface(new JavascriptInterface(), “javascriptInterface”); 在Js代码中就能直接通过“javascriptInterface”直接调用了该Native的类的方法
function showToast(toast) {
javascript:javascriptInterface.showToast(toast);
}
但是这个官方提供的解决方案在Android4.2之前存在严重的安全漏洞(乌云)。在Android4.2之后,加入了@JavascriptInterface才得到解决。所以考虑到兼容低版本的系统,JavascriptInterface并不适合。
WebChromeClient.onConsoleMessage()####
这是Android提供给Js调试在Native代码里面打印日志信息的API,同时这也成了其中一种Js与Native代码通信的方法。在Js代码中调用console.log(‘xxx’)方法。
console.log('log message that is going to native code')
就会在Native代码的WebChromeClient.consoleMessage()中得到回调。consoleMessage.message()获得的正是Js代码console.log(‘xxx’)的内容。
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
super.onConsoleMessage(consoleMessage);
String msg = consoleMessage.message();//JavaScript输入的Log内容
}
}
WebChromeClient.onJsPrompt()####
其实除了WebChromeClient.onJsPrompt(),还有WebChromeClient.onJsAlert()和WebChromeClient.onJsConfirm()。顾名思义,这三个Js给Native代码的回调接口的作用分别是展示提示信息,展示警告信息和展示确认信息。鉴于,alert和confirm在Js的使用率很高,所以JSBridge的解决方案中都倾向于选用onJsPrompt()。
Js中调用
window.prompt(message, value)
WebChromeClient.onJsPrompt()就会受到回调。onJsPrompt()方法的message参数的值正是Js的方法window.prompt()的message的值。
public class CustomWebChromeClient extends WebChromeClient {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 处理JS 的调用逻辑
result.confirm();
return true;
}
WebViewClient.shouldOverrideUrlLoading()####
这个方法的作用是拦截所有WebView的Url跳转。页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了
public class CustomWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isJsBridgeUrl(url)) {
// JSbridge的处理逻辑
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
前端核心调用
var url = 'jsbridge://doAction?title=分享标题&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';
var iframe = document.createElement('iframe');
iframe.style.width = '1px';
iframe.style.height = '1px';
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
setTimeout(function() {
iframe.remove();
}, 100);
通过修改window.location.href也可以达到发起网络请求的效果,但是有一个很严重的问题,就是如果我们连续多次修改window.location.href的值,在Native层只能接收到最后一次请求,前面的请求都会被忽略掉。所以JS端发起网络请求的时候,需要使用iframe,这样就可以避免这个问题
实现方案
Android:
https://github.com/lzyzsd/JsBridge
https://github.com/papastar/JsBridge(Vanke)
IOS
https://github.com/marcuswestin/WebViewJavascriptBridge
样例代码
//界面跳转
webView.registerHandler("route", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
WebViewResult result = new WebViewResult();
try {
//Router.getInstance().openUrl(data);
Uri uri = Uri.parse(data);
if(TextUtils.equals(uri.getScheme(),"zze")){
String lastPath = uri.getLastPathSegment();
}
result.setCode(WebViewResult.CODE_SUCCESS);
}catch (Exception e){
result.setCode(WebViewResult.CODE_FAIL);
}finally {
function.onCallBack(result.getResult());
}
//Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
//function.onCallBack("submitFromWeb exe, response data 中文 from Java");
}
});
//分享
webView.registerHandler("share", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
WebViewResult result = new WebViewResult();
try {
String url = JSONUtils.getString(data,"title","");
String desc = JSONUtils.getString(data,"desc","");
String link = JSONUtils.getString(data,"link","");
String imgurl = JSONUtils.getString(data,"imgurl","");
doShare(url,desc,link,imgurl);
result.setCode(WebViewResult.CODE_SUCCESS);
}catch (Exception e){
result.setCode(WebViewResult.CODE_FAIL);
}finally {
function.onCallBack(result.getResult());
}
}
});
//获取数据
webView.registerHandler("getdata", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
WebViewResult<User> result = new WebViewResult<>();
try {
String type = JSONUtils.getString(data,"type","");
if(TextUtils.equals("1",type)){
User user = new User();
user.name = "Papa";
user.phone = "134XXXXXXXX";
user.token = "755fgw4twtr1g5gewq";
user.userId = "5678995";
result.setData(user);
}
result.setCode(WebViewResult.CODE_SUCCESS);
}catch (Exception e){
result.setCode(WebViewResult.CODE_FAIL);
}finally {
function.onCallBack(result.getResult());
}
}
});
前端调用
//call native method
window.WebViewJavascriptBridge.callHandler(
'share'
, {'title': '分享标题','desc': '分享内容','link': '分享链接','imgurl': '分享图片链接'}
, function(responseData) {
document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
}
);
参考文档
https://juejin.im/entry/573534f82e958a0069b27646
http://blog.csdn.net/sbsujjbcy/article/details/50752595
网友评论