0%

混合开发:Android 与 JavaScript 的相互通信

现代移动应用开发已经广泛运用了 Hybrid 模式,催生了 Hybrid App(混合模式移动应用),即在原生壳中内嵌网页,部分功能由 HTML5 实现的移动应用。

这种方案能大幅减少开发时间和成本,易于更新维护。因此以淘宝、京东为代表的电商 app,经常使用网页实现需灵活更新的页面。

Hybrid 的基础是原生与网页的相互通信。在 Android 平台可以认为是 Java 和 JavaScript 的相互调用。

Android 调用 JavaScript

loadUrl

在 Android 中调用 JavaScript 最简单的方式是使用 WebView.loadUrl 方法。

loadUrl 用于加载给定的链接。在链接中使用 javascript 伪协议,浏览器将执行 : 后面的 JavaScript 代码。

下面这行代码执行了 JS 中的 showMessage 函数:

1
mWebView.loadUrl("javascript:showMessage('Hello, Web!')");

这种方式的缺点是无法取得返回值,适合不需要返回值的场景。

evaluateJavascript

Android 4.4 以后提供了 WebView.evaluateJavascript 方法。它用于异步执行 JavaScript,当返回值非空时执行回调,据说有着比 loadUrl 更高的执行效率。

下面这段代码同样执行了 JS 中的 showMessage 函数,并且能够在 onReceiveValue 方法中拿到返回值。

1
mWebView.evaluateJavascript("javascript:showMessage('Hello, Web!')", new ValueCallback<String>() {
2
  @Override
3
  public void onReceiveValue(String value) {
4
    // 打印返回值
5
    log.d(value);
6
  }
7
});

JavaScript 调用 Android

shouldOverrideUrlLoading

WebViewClient.shouldOverrideUrlLoading 用于拦截即将加载的链接,重写默认行为。

因此,我们可以事先约定一系列伪协议链接:

1
myapp://showToast?text=Hello

然后在 Android 中拦截 WebView 加载的链接。对于符合约定格式的链接,执行相应的操作。对于其他链接,保持默认的加载行为。

1
private static final String APP_SCHEME = "myapp:";
2
...
3
4
mWebView.setWebViewClient(new WebViewClient() {
5
    @Override
6
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
7
        if (url.startsWith(APP_SCHEME)) {
8
            // 解析链接
9
            urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
10
            // 执行相应操作...
11
            return true;
12
        }
13
        return false;
14
    }
15
});

在网页中调用时只需要当作一个重定向就够了。

1
window.location.href = 'myapp://showToast?text=Hello'

addJavascriptInterface

WebView.addJavascriptInterface 用于绑定接口,使 JavaScript 能够调用 Android 代码。

简单地说,我们在 Android 里声明接口。接口包含我们想要暴露给网页调用的方法,它以对象的形式注入到 JavaScript 的 Window 对象下。最后,JavaScript 调用接口对象的方法,执行接口中的原生代码。

首先创建一个接口类:

1
public class WebAppInterface {
2
3
  Context mContext;
4
5
  // 初始化接口并设置上下文
6
  WebAppInterface(Context c) {
7
    mContext = c;
8
  }
9
10
  // 显示短消息的接口
11
  @JavascriptInterface
12
  public void showToast(String text) {
13
    Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
14
  }
15
16
}

注意,Android 4.2 以上使用 @JavascriptInterface 注解的方法才会暴露给 JavaScript。

然后向 WebView 添加这个接口,并将其命名为 Android

1
mWebView.addJavascriptInterface(new WebAppInterface(this), "Android");

当网页加载完成时,WebView 也将注入一个 Window.Android 对象。

通过这个对象就可以调用接口中的原生方法了:

1
Android.showToast('Hello, Android!')

使用 JsBridge

JsBridge 是一个成熟的,用于原生和网页双向通信的开源库。它封装了上文提到的通信方式,并且提供包含传参、返回值和回调的完整方案。JsBridge 会向网页注入一个 Window.WebViewJavascriptBridge 对象。

JsBridge 的使用方法非常简单。

在布局文件中使用 com.github.lzyzsd.jsbridge.BridgeWebView 替代默认的 WebView 控件。

接着注册一个 Java 处理器:

1
webView.registerHandler("showToast", new BridgeHandler() {
2
  @Override
3
  public void handler(String data, CallBackFunction function) {
4
    // 执行原生代码...
5
    function.onCallBack("return value");
6
  }
7
});

在 JavaScript 中调用 Java 处理器:

1
WebViewJavascriptBridge.callHandler('showToast', 'Hello, Android!', responseData => {
2
  // 打印返回值
3
  console.log(responseData)
4
})

类似的,我们也可以注册 JavaScript 处理器:

1
WebViewJavascriptBridge.registerHandler('showMessage', (data, responseCallback) => {
2
  // 执行网页脚本...
3
  responseCallback('return value')
4
})

然后在 Java 中调用 JavaScript 处理器:

1
webView.callHandler("showMessage", "Hello, JavaScript!", new CallBackFunction() {
2
  @Override
3
  public void onCallBack(String data) {
4
    // 打印返回值
5
    log.d(data);
6
  }
7
});

访问 GitHub 了解更多 JsBridge 的使用说明。

相关环境:macOS 10.14 / Android Studio 3.3 / JsBridge 1.0