Hybrid 开发已经成为一种常态了,根据百度百科释义:
Hybrid App(混合模式移动应用)是指介于 web-app、native-app 这两者之间的 app,兼具“Native App 良好用户交互体验的优势”和“Web App 跨平台开发的优势”。
尽管我对「兼具“Native App 良好用户交互体验的优势”」持保守意见,但毋庸置疑,「跨平台开发的优势」让当今市场上绝大多数的应用都使用了 Hybrid 开发技术。
Hybrid 开发方案的流行,也衍生了许多第三方框架,如 QuickHybrid
等,但更多开发者仍然选择自行实现 Hybrid。
在 Android 中,Hybrid 开发就是使用 WebView
控件加载 H5 链接,由于 Android 端使用的语言是 Java 或 Kotlin,而 H5 端使用的是 JavaSrcipt,两者没有办法直接互相调用,所以 Hybrid 开发的核心实际上就是两者之间的控制桥。
JavaScript 调用 Android
因为是在移动平台使用,所以 H5 端调用原生平台的能力更为常用。
首先需要在 Android 端创建一个提供交互方法的类,如:
public class JsInterface {
@JavascriptInterface
public void toast(String msg) {
Toast.makeText(AppUtil.getContext(), msg, Toast.LENGTH_SHORT).show();
}
}
有三个注意的点:一是方法必须添加 @JavascriptInterface
注解;二是方法的参数仅可为 JavaScript 能够提供的类型,比如上方的的 Toast
需要传入 Context
对象,这个对象是 Android 平台提供的,总不能找 H5 要吧;三是方法的返回值同样也仅能为 JavaScript 可用类型,理由同上。
然后,我们配置 WebView
使其能够调用上面的方法:
public class WebActivity extends AppCompatActivity {
...
WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsInterface(), "android");
...
}
}
因为启用 JavaScript 可能会在应用程序中引入 XSS 漏洞,从而产生安全问题,所以 WebView
默认情况下是禁止 JavaScript 调用的,可以通过 setJavaScriptEnabled()
开启,然后调用 addJavascriptInterface()
将刚刚的交互类添加进来,该方法接收两个参数,第一个就是刚刚的交互类的对象,第二个参数是是 Android 端和 H5 端约定的一个字符串,通过这个约定的字符串“暗号”,能够让 H5 端通过 JavaScript 调用 Android 端提供的方法。
最后,在 H5 端调用即可:
<html>
<script type="text/javascript">
...
function toastMessage(message) {
android.toast(message)
}
</script>
...
</html>
调用方法就是使用刚刚约定好的字符串“暗号”拼接 .
以及 Android 端提供的方法名称即可。
Android 调用 JavaScript
Android 调用 JavaScript 几乎都是通过字符串操作的,不像 JavaScript 调用 Android 一样通过类似函数的写法,所以写起来比较容易出错。
无返回值调用
首先在 H5 端创建一个能够给 Android 端调用的方法:
<html>
<script type="text/javascript">
...
function alertMessage(msg) {
alert(msg)
}
</script>
...
</html>
然后 Android 端在合适的时机调用即可:
public class WebActivity extends AppCompatActivity {
WebView webView;
...
public void alertMessage(String msg) {
webView.loadUrl("javascript:alertMessage('" + msg + "')";);
}
}
同样是通过 loadUrl()
方法执行,传入 javascript:
拼接方法名称组成的字符串,需要两个点:一是如果有参数,同样要传入 JavaScript 能够处理的类型,理由同上文所述;二是假如传入的参数为字符串,可以使用单引号 '
代替双引号 "
,如果想要使用双引号 "
,记得转义。
有返回值调用
由于通过 loadUrl()
方法调用 JavaScript 函数,没有办法直接获取返回值。
Android 4.4 之前并没有提供直接调用 JavaScript 函数并获取值的方法,所以在此之前,常用的思路是 Java 调用 JavaScript 方法,JavaScript 方法执行完毕后 H5 端再次调用 Android 端代码将值返回。
但是现在,有了更好的方法。
首先依然是现在 H5 端创建一个能够给 Android 端调用的方法:
<html>
<script type="text/javascript">
...
function sum(m, n) {
return m + n
}
</script>
...
</html>
然后 Android 端在合适的时机调用即可:
public class WebActivity extends AppCompatActivity {
WebView webView;
...
public void sum(m, n) {
webView.evaluateJavascript("sum(" + m + ", " + n + ")", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
System.out.println(value);
}
});
}
}
改为使用 evaluateJavascript()
方法执行 JavaScript 函数,并在回调中获取返回值。
有两个需要注意的点:一是回调中限定了返回值的类型为 String
,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的 JSON 返回;二是 evaluateJavascript()
需要 UI 线程调用,因此 onReceiveValue()
回调也执行在主线程。