作为跨平台的解决方案之一,使用 webview 这种 hybrid 方式是最早被应用起来的,现在也已慢慢走向成熟。本文简单介绍一下使用 webview 过程中 js 和 native 的几种通信方式和性能对比。
通信方式介绍
Android webview 关于 Js 和 Native 的几种通信方式如下所示:
Native-Js
webview.loadUrl
Java 中调用 Js 代码,使用 loadUrl()
比较简单,如下所示条用弹出一个提示框:
1 | webView.loadUrl("javascript:alert('hello')"); |
Js 代码统一在页面加载完成,即 onPageFinished()
之后调用。
webview.evaluateJavaScript
在 Android 4.4(API ≥ 19)后,新增了在 Native 中调用 Js 代码的方式:
1 | webView.evaluateJavascript("(function() { return 'evaluateJavascript'; })()", new ValueCallback<String>() { |
这种方式优点:
- 执行效率高
- 有 Js 层执行后的回调
但要注意的是,这种方式不会触发页面的刷新(loadUrl()
加载页面时会触发刷新)。
Js-Native
addJavaScriptInterface
js 调用 native 效率最高的是使用 addJavaScriptInterface 方式,获取注入在 Js 中的 Java 对象,直接调用它的成员方法。
在 Java 层中只需要创建一个对象,并声明给 Js 层调用的方法,再添加即可。
1 | (Build.VERSION_CODES.JELLY_BEAN_MR1) |
Js 层调用:
1 | mWebViewJSInterface.invoke('hello') |
需要注意的是:addJavascriptInterface 的执行时机是,页面的下一次 load 时。所以一般可以将它放在页面加载之前进行注册。
shouldOverrideUrlLoading
Js 层可以通过 a 标签的跳转和 HTML 的请求有相关的响应,可以被 WebViewClient
的回调方法 shouldOverrideUrlLoading ()
拦截。这里我们可以利用 scheme iframe 机制,来实现类 shouldOverrideUrlLoading 的请求。
1 | var url = 'jsbridge://apollo/isApolloEngineReady?p=%7B%22callback%22%3A%22__MQQ_CALLBACK_AUTO_54%22%7D#54'; |
再在 Java 层给 webView 设置 WebViewClient 的回调,实现 shouldOverrideUrlLoading
拦截:
1 | mWebView.setWebViewClient(new WebViewClient(){ |
使用 loadUrl()
或 window.location
第一次加载页面时,shouldOverrideUrlLoading
是不会接收到回调的,但之后的每次页面请求都可以拦截到,可以用来判断是否为重定向。
onJsPromt、onJsAlert、onJsConfirm/onConsoleMessage
在 Android 中, WebChromeClient 回调会处理 Js 层调用 window 对象的 alert,confirm,prompt,console 方法,可以在拦截时进行定制化 UI 。
1 | public WebChromeClient mWebChromeClient = new WebChromeClient() { |
性能对比
综合以上 Js - Native 的通信方式,对比如下:
方案 | 性能 | 适用范围 | 有无返回值 |
---|---|---|---|
addJavaScriptInterface | 高,直接调用native方法,无需模式匹配 | 4.2及以上版本 | 可以返回任意值 |
shouldOverrideUrlLoading | 低,通过iframe或者script方式发起url请求 | 兼容性好 | 无返回值 |
onJsPromt/console.log | 较快 | 兼容性一般 | 返回值只有true or false |
webview.loadUrl() | 慢,会刷新触发新页面 | 所有版本 | 无 |
webview.evaluateJavaScript() | 快 | 4.4以上版本 | 有返回值 |
对于 Native 处理后想给 Js 回调:
addJavaScriptInterface 或 onJsPromt/console.log 这俩种方式有同步直接返回值,shouldOverrideUrlLoading 是不能直接返回的。当然为了保持前端抒写一致性,这里需要统一处理异步回调。
Js 传入 Url 参数时就传入唯一的自增的 callbackId 方法的名字,Native 层处理后再通过
loadUrl()
或evaluateJavascript()
通过 window 对象调用全局监听的 callbackId 方法,以此完成异步回调。