我们知道使用 WebViewClient 和 WebChromeClient 类可以响应发生在 WebView 里的特定事件。然而,通过注入任意 Java 对象到 WebView 的 JavaScript 中,我们还可以做到更多。查阅 WebView 文档,找到 addJavascriptInterface (Object object, String name) 方法。使用该方法,可以将任意 Java 对象注入到指定文档中。
1. API 简介
方法 | |
void addJavascriptInterface (Object object, String name) 将 Java 对象注入到 WebView 中。该对象会被注入主框架的 JavaScript 上下文,并将对象名暴露给 JavaScript。这样就可以从 JavaScript 中访问 Java 对象的方法。对于面向 API 级 JELLY_BEAN_MR1 及更高版本的应用程序,JavaScript 只能访问被 JavaScriptInterface 注释的公共方法。 对于 API 级 JELLY_BEAN 或更低级别的应用程序,可以访问所有公共方法(包括继承的方法),相关含义,请参阅下面的重要安全说明。 | |
参数 | |
object | Object: Java 对象,用于注入到 WebView 的 JavaScript 上下文。 空值被忽略。 |
name | String: 暴露给 JavaScript 的对象名称。 |
重要
- 此方法允许通过 JavaScript 控制主程序。这是一项非常强大的功能,但也为面向 JELLY_BEAN 或更早版本的应用带来了安全风险。如果应用程序在运行在低于 4.2 的 Android 设备上 ,即使面向 API 级别高于 JELLY_BEAN,应用程序仍然存在漏洞。使用此方法最安全的方式是面向 JELLY_BEAN_MR1,并确保仅在 Android 4.2 或更高版本上运行时才调用该方法。使用这些旧版本,JavaScript 可以使用反射来访问注入对象的公共字段。在包含不可信内容的 WebView 中使用此方法可能允许攻击者以非预期的方式操纵主应用程序,并使用主应用程序的权限执行 Java 代码。 在可能包含不可信内容的 WebView 中使用此方法时要格外小心。
- JavaScript 在 WebView 的专用后台线程上与 Java 对象交互。因此需要注意保持线程安全。
- Java对象的字段不可访问。
- 对于面向 API 级别 LOLLIPOP 及更高版本的应用程序,被注入的 Java 对象的方法可以通过 JavaScript 进行枚举。
2. 简明教程
请注意,注入的对象在下次(重新)加载页面之前不会显示在 JavaScript 中。 在注入对象之前应该启用 JavaScript。 例如:
1 | class JsObject { |
3. 精简案例
在 Java 代码中找到 WebView 并设置 JavaScript 可用,为 WebView 添加 JavaScript 接口,传入要注入的对象和对象名称。本例中所传的对象为一个匿名内部类,并增加一个公共方法 send(String message) 。因为本例中 targetSdkVersion 为 27,API 级别高于 JELLY_BEAN_MR1(API 17),所以,要想 send(String message) 方法被 JavaScript 访问,那么该方法就必须被 @JavascriptInterface 注释。为测试方便,WebView 默认加载 assets 目录下的 demo.html 页面。
1 | package com.sunzn.script; |
在 Html 代码的 head 中定义 JavaScript 方法 sendToAndroid(message),并由页面控件 button 点击调用。在 sendToAndroid(message) 方法中,JavaScript 持有注入的 Java 对象,并执行该对象的 send(String message) 方法。这样,在点击 button 的时候,Java 代码中的 send(String message) 方法就会接到来自网页的 message : In Android Land,并将该信息输出到控制台。
注意:JavaScript 方法中的 Java 对象名称必须和 Java 代码中暴露给 JavaScript 的对象名称一致。
1 | <html xmlns:width="http://www.w3.org/1999/xhtml"> |
4. 项目地址
项目地址:JavaScript @ Github