- Java 调用 JavaScript (JS):在 Android App 的 Java/Kotlin 代码中,执行 WebView 内部定义的 JS 函数。
- JavaScript 调用 Java (JS → Java):在 WebView 的 JS 代码中,调用 Android App 的 Java/Kotlin 方法。
准备工作:启用 JavaScript
在进行任何交互之前,必须确保 WebView 的 JavaScript 功能是开启的。

// 在 Activity 或 Fragment 中初始化 WebView WebView webView = findViewById(R.id.webview); // 启用 JavaScript WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); // (可选) 但推荐:允许混合内容 (HTTP 和 HTTPS) webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_MODE_COMPATIBILITY);
Part 1: Java 调用 JavaScript
这是最直接的场景,你的 App 有一个 WebView 加载了一个网页,你想从 App 的代码里执行网页里的某个 JS 函数。
核心方法:evaluateJavascript()
这是 Android 推荐使用的现代方法,它允许你执行一段任意的 JavaScript 代码,并且可以获取到代码执行后的返回值。
方法签名:
void evaluateJavascript(String script, ValueCallback<String> resultCallback)
script: 你想执行的 JavaScript 代码字符串。resultCallback: 一个回调,用于接收 JS 代码执行后的返回值,注意,返回值总是以字符串形式传递,即使 JS 返回的是数字、对象等。
示例:调用 JS 无参函数
假设你的 HTML/JS 文件 (index.html) 内容如下:

<!DOCTYPE html>
<html>
<head>JS 交互 Demo</title>
<script>
function showToastFromJS(message) {
alert("来自 WebView 的消息: " + message);
}
function getSystemInfo() {
// 可以返回一些信息给 Android
return "当前系统时间: " + new Date().toLocaleString();
}
</script>
</head>
<body>
<h1>WebView JS 交互 Demo</h1>
<p>请在 Android 代码中调用 showToastFromJS() 函数。</p>
</body>
</html>
在 Android 代码中调用它:
// 加载本地 HTML 文件 (将 index.html 放在 assets 目录下)
webView.loadUrl("file:///android_asset/index.html");
// 定义一个按钮来触发调用
Button callJsButton = findViewById(R.id.call_js_button);
callJsButton.setOnClickListener(v -> {
// 1. 调用无参、无返回值的 JS 函数
String jsToExecute1 = "showToastFromJS('你好,来自 Android!')";
webView.evaluateJavascript(jsToExecute1, value -> {
// value 是回调结果,对于无返回值的函数,通常是 null
Log.d("WebView", "JS 执行结果: " + value);
});
// 2. 调用有返回值的 JS 函数
String jsToExecute2 = "getSystemInfo()";
webView.evaluateJavascript(jsToExecute2, value -> {
// value 是 JS 函数的返回值,注意是字符串类型
// 需要手动去除两端的引号
String result = value.replace("\"", "");
Log.d("WebView", "JS 返回的系统信息: " + result);
// 也可以在 UI 线程更新 UI
runOnUiThread(() -> {
Toast.makeText(this, "JS 返回: " + result, Toast.LENGTH_SHORT).show();
});
});
});
关键点:
- 线程安全:
evaluateJavascript必须在 UI 线程 调用。 - 返回值处理:JS 返回的任何值都会被包裹在双引号 中,你需要手动处理(如
value.replace("\"", ""))来获取原始数据。 - 错误处理:JS 代码执行出错,错误信息会通过
resultCallback返回,通常是一个包含错误信息的字符串。
Part 2: JavaScript 调用 Java
这个功能更强大,它让网页能够与 App 的原生能力进行交互(获取用户信息、调用相机、使用原生 UI 组件等)。
这个过程分三步:
- 在 Java 中创建一个与 JS 交互的接口对象。
- 将这个 Java 对象映射(绑定)到 JavaScript 的一个全局变量名上。
- 在 JavaScript 代码中,通过这个全局变量名来调用 Java 对象的方法。
核心方法:addJavascriptInterface()
方法签名:
void addJavascriptInterface(Object object, String name)
object: 你的 Java 对象,这个对象中@JavascriptInterface注解的方法可以被 JS 调用。name: 在 JavaScript 中可以访问到这个 Java 对象的全局变量名。
示例:从 JS 调用 Android 的 Toast 和获取设备信息
第一步:创建一个 Java 接口类
这个类将定义可以被 JS 调用的方法。重要:从 Android 4.2 (API 17) 开始,所有被 JS 调用的方法都必须用 @JavascriptInterface 注解,这是为了安全考虑,防止恶意网页通过反射调用你不想暴露的 Java 方法。
import android.annotation.SuppressLint;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
public class WebAppInterface {
private Context mContext;
// 构造函数,传入 Context
public WebAppInterface(Context context) {
this.mContext = context;
}
// 定义一个可以被 JS 调用的方法
// @JavascriptInterface 注解是必须的
@JavascriptInterface
public void showToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
// 再定义一个方法,获取 Android 版本
@JavascriptInterface
public String getAndroidVersion() {
return "Android 版本: " + android.os.Build.VERSION.RELEASE;
}
}
第二步:在 Activity/Fragment 中绑定接口
在 WebView 初始化后,将你创建的接口对象添加到 WebView 中。
WebView webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
// 创建 WebAppInterface 的实例
WebAppInterface webAppInterface = new WebAppInterface(this);
// 将 Java 对象映射到 JS 的全局变量 "Android"
// 之后在 JS 中就可以通过 Android.showToast() 来调用了
webView.addJavascriptInterface(webAppInterface, "Android");
// 加载包含 JS 调用代码的 HTML
webView.loadUrl("file:///android_asset/index.html");
第三步:在 JavaScript 中调用 Java 方法
修改你的 index.html 文件,让它包含调用 Java 方法的按钮或逻辑。
<!DOCTYPE html>
<html>
<head>JS 交互 Demo</title>
<script>
function callAndroidToast() {
// 通过 "Android" 这个全局变量名调用 Java 的 showToast 方法
// 并传递一个字符串参数
Android.showToast("你好,JS 成功调用了 Android!");
}
function getDeviceInfo() {
// 调用 Java 的 getAndroidVersion 方法
// 由于这个方法有返回值,我们可以接收它
var version = Android.getAndroidVersion();
alert(version);
}
</script>
</head>
<body>
<h1>WebView JS 交互 Demo</h1>
<button onclick="callAndroidToast()">调用 Android Toast</button>
<br><br>
<button onclick="getDeviceInfo()">获取 Android 版本</button>
</body>
</html>
当你在 App 中加载这个 HTML 页面,并点击按钮时,就会触发相应的 Java 代码执行。
安全警告 (非常重要!)
使用 addJavascriptInterface 存在严重的安全风险,尤其是在加载任意、不受信任的网页时。
漏洞原理 (Android 4.1 及以下版本):
在 Android 4.1 (API 16) 及更早的版本中,存在一个远程代码执行漏洞,攻击者可以通过精心构造的 HTML 页面,利用 addJavascriptInterface 接口,反射调用你 App 中的任意 Java 类和方法,最终可能导致恶意代码执行,窃取用户数据。
如何防范?
-
升级系统版本:这是最根本的解决方法,Google 从 Android 4.2 (API 17) 开始,通过
@JavascriptInterface注解机制修复了这个漏洞,只有被明确注解的方法才能被 JS 调用。 -
谨慎使用
addJavascriptInterface:- 只加载可信内容:绝对不要对来自互联网的、不可信的 URL 使用
addJavascriptInterface,它应该只用于加载你 App 内部的、完全受控的 HTML/JS 资源(放在assets或res/raw目录下)。 - 最小化暴露接口:你提供给 JS 的接口对象应该尽可能简单,只暴露必要的功能,不要把整个 Activity 或一个功能庞大的工具类直接暴露出去。
- 只加载可信内容:绝对不要对来自互联网的、不可信的 URL 使用
-
使用
shouldOverrideUrlLoading作为额外的安全层: 你可以重写 WebView 的shouldOverrideUrlLoading方法来拦截所有 URL 加载请求,URL 不是你期望的(不是以file://或https://yourdomain.com开头),你可以阻止它加载。webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 只允许加载本地文件和特定域名 if (url.startsWith("file:///android_asset/") || url.startsWith("https://your-trusted-domain.com")) { return false; // false 表示由 WebView 自己处理 } else { // 对于其他 URL,可以选择在系统浏览器中打开或直接阻止 // Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); // startActivity(intent); return true; // true 表示由应用处理,这里我们直接阻止 } } });
| 交互方向 | 核心方法 | 用途 | 注意事项 |
|---|---|---|---|
| Java → JS | evaluateJavascript() |
执行 JS 代码,可获取返回值 | 必须在 UI 线程调用,返回值为字符串。 |
| JS → Java | addJavascriptInterface() |
将 Java 对象暴露给 JS,使其可调用 | 存在安全风险! 只能加载可信内容,目标方法必须有 @JavascriptInterface 注解。 |
通过这两种方式,你可以构建出功能强大、体验丰富的混合型 Android 应用。
