杰瑞科技汇

android java 调用js

核心概念

Android 和 JavaScript 运行在不同的环境中:

android java 调用js-图1
(图片来源网络,侵删)
  • Android (Java/Kotlin): 运行在 Dalvik/ART 虚拟机中,拥有应用的生命周期、UI 线程等。
  • JavaScript: 运行在 WebView 内部的 V8 (Chrome) 引擎中,是单线程的。

WebView 就像一个桥梁,连接了这两个世界。evaluateJavascript() 方法允许我们从 Android 端向桥的另一端(WebView 内部)发送一段 JavaScript 代码并执行,同时还能获取到执行结果。


基础步骤与核心代码

第一步:在 XML 布局文件中添加 WebView

<!-- activity_main.xml -->
<WebView
    android:id="@+id/my_webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

第二步:在 Activity/Fragment 中获取 WebView 实例并设置基本属性

import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
    private WebView webView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = findViewById(R.id.my_webview);
        // 必须设置,否则会在默认浏览器中打开链接
        webView.setWebViewClient(new WebViewClient());
        // 启用 JavaScript
        webView.getSettings().setJavaScriptEnabled(true);
        // 加载本地 HTML 文件或远程 URL
        // 加载本地 assets 目录下的 HTML 文件
        webView.loadUrl("file:///android_asset/my_web_page.html");
        // 或者加载远程网页
        // webView.loadUrl("https://www.example.com");
    }
}

关键点:

  • webView.getSettings().setJavaScriptEnabled(true); 必须调用,否则 evaluateJavascript() 不会生效。
  • webView.setWebViewClient(new WebViewClient()); 确保链接在 WebView 内部打开。

第三步:调用 evaluateJavascript() 方法执行 JS 代码

这是最核心的一步。evaluateJavascript 的签名如下:

// 第一个参数:要执行的 JavaScript 代码字符串
// 第二个参数:ValueCallback,用于接收 JS 代码的执行结果
public void evaluateJavascript(String script, ValueCallback<String> resultCallback)

示例 1:调用一个无参数、无返回值的 JS 函数

android java 调用js-图2
(图片来源网络,侵删)

假设我们的 HTML 文件中有这样一个函数:

// my_web_page.html
<script>
    function showToast() {
        alert("Hello from JavaScript!");
    }
</script>

在 Java 中调用它:

// 在 Activity 中
webView.evaluateJavascript("showToast()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        // value 是 JS 函数的返回值。
        // 对于 alert() 这种没有返回值的函数,value 通常是 "null"。
        // 这个回调在 UI 线程执行。
        System.out.println("JS function executed, returned: " + value);
    }
});

示例 2:调用一个带参数,并返回值的 JS 函数

假设 JS 函数如下:

android java 调用js-图3
(图片来源网络,侵删)
// my_web_page.html
<script>
    function addNumbers(a, b) {
        return a + b;
    }
</script>

在 Java 中调用它,并处理返回值:

// 在 Activity 中
String jsCode = "addNumbers(5, 10);";
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String result) {
        // result 是一个字符串,包含了 JS 函数的返回值
        // 这里 result 会是 "15"
        try {
            int sum = Integer.parseInt(result);
            System.out.println("The sum is: " + sum);
            // 你可以在这里更新 UI
            runOnUiThread(() -> {
                // 更新一个 TextView
                // textView.setText("Sum from JS: " + sum);
            });
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }
});

完整示例

my_web_page.html (放在 app/src/main/assets/ 目录下)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">Android JS Interaction</title>
    <style>
        body { font-family: sans-serif; text-align: center; padding-top: 50px; }
        button { font-size: 18px; padding: 10px 20px; margin: 10px; }
        #result { font-size: 24px; color: green; margin-top: 20px; }
    </style>
</head>
<body>
    <h1>WebView JavaScript Bridge</h1>
    <button id="androidCallBtn">Call Android Method</button>
    <button id="jsCallBtn">Call JS Method (from Android)</button>
    <div id="result"></div>
    <script>
        // JS 函数,供 Android 调用
        function showToastFromAndroid(message) {
            document.getElementById('result').innerText = "Message from Android: " + message;
            // 也可以用 alert
            // alert(message);
        }
        // JS 函数,带返回值,供 Android 调用
        function getGreeting() {
            return "Hello from JavaScript!";
        }
        // JS 函数,用于 Android 调用并获取返回值
        function addNumbers(a, b) {
            return a + b;
        }
        // 当页面加载完成后,监听按钮点击事件
        document.addEventListener('DOMContentLoaded', function() {
            document.getElementById('androidCallBtn').addEventListener('click', function() {
                // 调用 Android 的全局方法
                if (window.AndroidInterface) {
                    window.AndroidInterface.showToast("Hello from JavaScript!");
                } else {
                    document.getElementById('result').innerText = "AndroidInterface not found!";
                }
            });
            document.getElementById('jsCallBtn').addEventListener('click', function() {
                document.getElementById('result').innerText = "This button is for Android to call me.";
            });
        });
    </script>
</body>
</html>

MainActivity.java

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private WebView webView;
    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        webView = findViewById(R.id.my_webview);
        // 1. 设置 WebViewClient
        webView.setWebViewClient(new WebViewClient());
        // 2. 启用 JavaScript
        webView.getSettings().setJavaScriptEnabled(true);
        // 可选:处理 JS 的 alert, prompt, confirm 对话框
        webView.setWebChromeClient(new WebChromeClient());
        // 3. 添加 JavaScript 接口(让 JS 能调用 Android)
        // "AndroidInterface" 是 JS 中调用的对象名称
        webView.addJavascriptInterface(new WebAppInterface(), "AndroidInterface");
        // 4. 加载 HTML
        webView.loadUrl("file:///android_asset/my_web_page.html");
    }
    // 5. 定义一个内部类作为 JavaScript 接口
    public class WebAppInterface {
        // @JavascriptInterface 注解是必须的,否则无法从 JS 访问此方法
        // 从 Android 4.2 (API 17) 开始强制要求
        @JavascriptInterface
        public void showToast(String toast) {
            // 在这里执行 Android 代码
            // 注意:这个回调在非 UI 线程,如果需要更新 UI,必须使用 runOnUiThread
            runOnUiThread(() -> Toast.makeText(getApplicationContext(), toast, Toast.LENGTH_SHORT).show());
        }
    }
    // 6. 在 Activity 的其他地方调用 JS 函数
    public void callJsFunction() {
        // 示例:调用 JS 的 showToastFromAndroid 方法
        webView.evaluateJavascript("showToastFromAndroid('Data received from Android!')", null);
        // 示例:调用 JS 的 getGreeting 方法并处理返回值
        webView.evaluateJavascript("getGreeting()", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                // value 会是 "Hello from JavaScript!"
                runOnUiThread(() -> Toast.makeText(MainActivity.this, "JS returned: " + value, Toast.LENGTH_LONG).show());
            }
        });
        // 示例:调用 JS 的 addNumbers 方法
        webView.evaluateJavascript("addNumbers(100, 200)", new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String result) {
                // result 会是 "300"
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "Sum from JS: " + result, Toast.LENGTH_SHORT).show();
                });
            }
        });
    }
    // 别忘了在 Activity 销毁时销毁 WebView
    @Override
    protected void onDestroy() {
        if (webView != null) {
            webView.destroy();
            webView = null;
        }
        super.onDestroy();
    }
}

重要注意事项与最佳实践

线程问题

  • evaluateJavascript() 的回调 onReceiveValue() 是在 UI 线程 执行的,所以你不需要手动切换到 UI 线程来更新 UI。
  • 通过 JavascriptInterface 从 JS 调用过来的方法(如上面的 showToast)是在 非 UI 线程 执行的,如果你需要更新 UI(如 Toast, TextView),必须使用 Activity.runOnUiThread()

@JavascriptInterface 注解

  • 从 Android 4.2 (API 17) 开始,所有暴露给 JavaScript 的 Java 方法都必须使用 @JavascriptInterface 注解,否则,它们在 JS 端将不可见,这是一个重要的安全措施。

返回值处理

  • evaluateJavascript 的回调接收的返回值永远是 String 类型。
  • JS 函数没有返回值,返回值通常是 "null"
  • JS 函数抛出异常,返回值会是一个以 Uncaught Error: 开头的错误信息字符串。

安全性(非常重要!)

  • 不要信任来自 WebView 的任何数据,JS 可能被恶意注入(加载了不安全的远程网页)。
  • JavascriptInterface 的方法中,永远不要执行以下操作:
    • 执行任意文件路径拼接。
    • 执行任意 shell 命令。
    • 暴露敏感信息(如密码、token)。
  • 对所有来自 JS 的输入进行严格的校验和过滤。

WebView 的生命周期

  • ActivityonDestroy() 中,务必调用 webView.destroy() 来释放资源,防止内存泄漏。

从 JavaScript 调用 Android (反向调用)

这在上面的完整示例中已经展示了,是 addJavascriptInterface 的用途。

步骤:

  1. 创建一个 Java 类,包含你希望 JS 调用的方法。
  2. 在这些方法上加上 @JavascriptInterface 注解。
  3. WebView 实例上调用 addJavascriptInterface(yourObject, "jsObjectName"),将你的对象暴露给 JS。
  4. 在 JS 中,就可以通过 window.jsObjectName.yourMethod() 来调用。

JS 代码示例:

// 调用 Android 的 showToast 方法
window.AndroidInterface.showToast("Hello, Android!");
功能 方法 说明
Android 调用 JS webView.evaluateJavascript("jsCode()", callback) 执行任意 JS 代码,并能获取返回值。
JS 调用 Android webView.addJavascriptInterface(new MyInterface(), "MyInterface") 在 JS 中通过 window.MyInterface.method() 调用。
必备设置 webView.getSettings().setJavaScriptEnabled(true); 启用 JS 支持。
安全注解 @JavascriptInterface 必须加在暴露给 JS 的方法上 (API 17+)。
线程安全 runOnUiThread() JavascriptInterface 回调中更新 UI 时使用。

通过以上方法和注意事项,你就可以在 Android 应用中灵活地与 WebView 中的 JavaScript 进行双向通信了。

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