杰瑞科技汇

WebView中Java如何调用JS方法?

  1. Java 调用 JavaScript (JS):在 Android App 的 Java/Kotlin 代码中,执行 WebView 内部定义的 JS 函数。
  2. JavaScript 调用 Java (JS → Java):在 WebView 的 JS 代码中,调用 Android App 的 Java/Kotlin 方法。

准备工作:启用 JavaScript

在进行任何交互之前,必须确保 WebView 的 JavaScript 功能是开启的。

WebView中Java如何调用JS方法?-图1
(图片来源网络,侵删)
// 在 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) 内容如下:

WebView中Java如何调用JS方法?-图2
(图片来源网络,侵删)
<!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 组件等)。

这个过程分三步:

  1. 在 Java 中创建一个与 JS 交互的接口对象。
  2. 将这个 Java 对象映射(绑定)到 JavaScript 的一个全局变量名上。
  3. 在 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 类和方法,最终可能导致恶意代码执行,窃取用户数据。

如何防范?

  1. 升级系统版本:这是最根本的解决方法,Google 从 Android 4.2 (API 17) 开始,通过 @JavascriptInterface 注解机制修复了这个漏洞,只有被明确注解的方法才能被 JS 调用。

  2. 谨慎使用 addJavascriptInterface

    • 只加载可信内容:绝对不要对来自互联网的、不可信的 URL 使用 addJavascriptInterface,它应该只用于加载你 App 内部的、完全受控的 HTML/JS 资源(放在 assetsres/raw 目录下)。
    • 最小化暴露接口:你提供给 JS 的接口对象应该尽可能简单,只暴露必要的功能,不要把整个 Activity 或一个功能庞大的工具类直接暴露出去。
  3. 使用 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 应用。

分享:
扫描分享到社交APP
上一篇
下一篇