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