Android Native 和 H5 交互

jsbridge

使用 @JavascriptInterface 注解的方式

Android 4.2 版本之前的 WebView.addJavascriptInterface() 接口引起的漏洞,可能导致恶意网页通过 Js 方法遍历刚刚通过 addjavascriptInterface 注入进来的类的所有方法从中获取到 getClass 方法,然后通过反射获取到 Runtime 对象,进而调用 Runtime 对象的 exec 方法执行一些操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
function execute(cmdArgs) {
for (var obj in window) {
if ("getClass" in window[obj]) {
alert(obj);
return window[obj]
.getClass()
.forName("java.lang.Runtime")
.getMethod("getRuntime", null)
.invoke(null, null)
.exec(cmdArgs);
}
}
}

Native 端代码:

1
2
3
4
5
6
7
8
9
10
webSettings.setJavaScriptEnabled(true);
//向 H5 注入一个全局对象供 js 调用
webView.addJavascriptInterface(new JsBridge(){
@JavascriptInterface
public int sum(int a, int b) {
return a+b;
}
}, "jsBridge");
//调用 js 中的方法
webView.loadUrl("javascript:sayHello()");
  1. Native 中的方法是供 H5 调用的,不能混淆,不能更改,耦合性高。
  2. 因为 APP 是宿主,可以直接访问 H5,就是在 H5 中曝露一些全局对象(包括方法),然后在原生 app 中调用这些对象。

H5 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<script type="text/javascript">
function sayHello() {
alert("Hello");
}

function alertMessage(message) {
alert(message);
}

function toastMessage(message) {
window.jsBridge.toastMessage(message);
}

function sum(a, b) {
window.jsBridge.onSumResult(a + b);
}
</script>
Java-Javascript Interaction In Android
</html>

//通过 native 注入的对象,调用 native 方法 window.jsBridge.sum(1, 2);

优点:

  1. 官方推荐;
  2. 实现方便简单;

缺点:

  1. 4.2(api 17) 以下存在安全隐患;
  2. 兼容性差;
  3. 不能混淆,方法名不能更改,耦合性太高;

自定义路由协议

scheme://host:port/path?query

H5 请求消息:
jsbridge://app.demo.com:111/login?params={“channel”:”h5”}

  • jsbridge://app.demo.com 为 JsBridge 标识;
  • 111 为 H5 请求码,用于区分同一个方法的不同请求。H5 自定义,Native 会原样返回;
  • login 为本地方法;
  • params 为请求参数,Json 格式。参数可能需要 URL 编码;

Native 响应消息:

1
2
3
4
5
6
7
8
9
{
"code": 0,
"message": "success",
"function": "login",
"requestCode": 111,
"data": {
"username": "AAA"
}
}
  • code 为响应码。0 成功;1 失败。
  • message 为响应消息。
  • function 为 Native 端处理完成后,将消息返回给 H5 的回调方法。
  • requestCode 是 H5 请求码。
  • data 字段为具体的业务字段,需提前约定好。

大致有如下几个步骤:

  1. 约定接口,如 jsbridge://app.demo.com:111/login?params={"channel":"h5"},H5 调用 window.prompt() 传递给 Native;
  2. Native 在 WebChromeClient#onJsPrompt() 方法中接收到 H5 传递的 Json 字符串;
  3. Native 解析字符串获取到请求方法,参数等,反射获取类(类名,参数要预先定义好)中的方法。方法处理后生成响应 Json 字符串;
  4. 最后响应字符串通过调用 WebView#evaluateJavascript() 方法参数传递给 H5。

Js 中对应的 window.alert()、window.confirm()、window.prompt()这三个方法的调用在 WebChromeClient 中都有对应的回调方法,分别为:
onJsAlert()、onJsConfirm()、onJsPrompt(),对于它们传入的 message,都可以在相应的回调方法中接收到。所以,对于 Js 调 Native 方法,我们可以借助这个信道,和前端协定好一段特定规则的 message。

跨平台解决框架

React Native

  1. Facebook 出品;
  2. JavaScript 语言;
  3. JSCore 引擎;
  4. React 设计模式;
  5. 原生渲染;

React Native 是利用 JS 来调用 Native 端的组件,从而实现相应的功能。

如下图所示,RN 的跨平台实现主要由三层构成,其中 C++ 实现的动态连结库(.so),作为中间适配层桥接,实现了 js 端与原生端的双向通信交互。这里最主要是封装了 JavaScriptCore 执行 js 的解析,而 react native 运行在 JavaScriptCore 中,所以不存在浏览器兼容的问题。

Alt text

Weex

  1. Alibaba 出品;
  2. JavaScript 语言;
  3. JS V8 引擎;
  4. Vue 设计模式;
  5. 原生渲染。

Flutter

  1. Google 出品;
  2. Dart 语言;
  3. Flutter Engine 引擎;
  4. 响应式设计模式;
  5. 原生渲染。

与 react native 和 weex 的通过 Javascript 开发不同,Flutter 的编程语言是 Dart,所以执行时并不需要 Javascript 引擎,但实际效果最终也通过原生渲染。

Alt text

参考

[1] cxzl/JSBridge
[2] lzyzsd/JsBridge
[3] Native 与 H5 交互的那些事
[4] Sunzxyong/JsBridge
[5] pengwei1024/JsBridge
[6] 最火跨平台 React Native+weex+Flutter