iOS UIWebView
iOS 侧代码:
// 获取 JS 上下文
JSContext *context = [webview valueForKeyPath:@“documentView.webView.mainFrame.javaScriptContext”];
// 注入 Block
context[@“callHandler”] = ^(JSValue * data) {
// 处理调用方法和参数
// 调用 Native 功能
// 回调 JS Callback
}
JS 代码:
window.callHandler(JSON.stringify({
type: “scan”,
data: “”,
callback: function(data) {
}
}));
这种方式的牛逼之处在于,JS 调用是同步的,可以立马拿到返回值。
我们也不再需要像拦截方式一样,每次传值都要把对象做 JSON.stringify
,可以直接传 JSON 过去,也支持直接传一个函数过去。
iOS WKWebView
WKWebView 里面通过 addScriptMessageHandler
来注入对象到 JS 上下文,可以在 WebView 销毁的时候调用 removeScriptMessageHandler
来销毁这个对象。
前端调用注入的原生方法之后,可以通过 didReceiveScriptMessage
来接收前端传过来的参数。
WKWebView *wkWebView = [[WKWebView alloc] init];
WKWebViewConfiguration *configuration = wkWebView.configuration;
WKUserContentController *userCC = configuration.userContentController;
// 注入对象
[userCC addScriptMessageHandler:self name:@“nativeObj”];
// 清除对象
[userCC removeScriptMessageHandler:self name:@“nativeObj”];
// 客户端处理前端调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
// 获取前端传来的参数
NSDictionary *msgBody = message.body;
// 如果是 nativeObj 就进行相应处理
if (![message.name isEqualToString:@“nativeObj”]) {
//
return;
}
}
使用 addScriptMessageHandler
注入的对象实际上只有一个 postMessage
方法,无法调用更多自定义方法。前端的调用方式如下:
window.webkit.messageHandlers.nativeObj.postMessage(data);
需要注意的是,这种方式要求 iOS8 及以上,而且返回不是同步的。和 UIWebView 一样的是,也支持直接传 JSON 对象,不需要 stringify。
Android addJavascriptInterface
安卓4.2之前注入 JS 一般是使用 addJavascriptInterface
,和前面的 addScriptMessageHandler
有一些类似,但又没有它的限制。
public void addJavascriptInterface() {
mWebView.addJavascriptInterface(new DatePickerJSBridge(), “DatePickerBridge”);
}
private class PickerJSBridge {
public void _pick(…) {
}
}
在 JS 里面调用:
window.DatePickerBridge._pick(…)
但这种方案有一定风险,可以参考这篇文章:WebView中接口隐患与手机挂马利用
在 Android4.2 之后提供了 @JavascriptInterface
注解,暴露给 JS 的方法必须要带上这个。
所以前面的 _pick
方法需要带上这个注解。
private class PickerJSBridge {
@JavascriptInterface
public void _pick(…) {
}
}
Native 调用 JS
Native 调用 JS 一般就是直接 JS 代码字符串,有些类似我们调用 JS 中的 eval
去执行一串代码。一般有 loadUrl
、evaluateJavascript
等几种方法,这里逐一介绍。
但是不管哪种方式,客户端都只能拿到挂载到 window
对象上面的属性和方法。
Android
在 Android 里面需要区分版本,在安卓4.4之前的版本支持 loadUrl,使用方式类似我们在 a 标签的 href
里面写 JS 脚本一样,都是javascript:xxx
的形式。
这种方式无法直接获取返回值。
webView.loadUrl(“javascript:foo()”)
在安卓4.4以上的版本一般使用 evaluateJavascript
这个 API 来调用。这里需要判断一下版本。
if (Build.VERSION.SDK_INT > 19) //see what wrapper we have
{
webView.evaluateJavascript(“javascript:foo()”, null);
} else {
webView.loadUrl(“javascript:foo()”);
}