WebView和JavaScrip交互基础

在 Android 开发中,WebView 是一个强大的组件,不仅可以加载网页或 HTML 内容,还能通过 JavaScript 与原生 Android 代码进行双向交互。这种交互允许 WebView 中的网页调用 Android 方法,或 Android 代码调用网页中的 JavaScript 函数,从而实现混合应用(Hybrid App)的功能。以下是 WebView 与 JavaScript 交互的基础指南,涵盖基本用法、代码示例、注意事项和实践建议。


一、WebView 与 JavaScript 交互概述

  • 目标
  • JavaScript 调用 Android:网页中的 JavaScript 代码调用 Android 原生方法(如显示 Toast、访问本地数据)。
  • Android 调用 JavaScript:Android 代码执行网页中的 JavaScript 函数(如传递数据、触发事件)。
  • 核心机制
  • 使用 addJavascriptInterface 暴露 Android 方法给 JavaScript。
  • 使用 WebView.loadUrl("javascript:...")WebView.evaluateJavascript 执行 JavaScript 代码。
  • 适用场景
  • 混合应用开发(如 H5 页面调用原生功能)。
  • 动态更新 UI(如通过 JavaScript 修改网页内容)。
  • 数据交互(如 Android 传递数据给网页,或网页返回数据)。
  • 安全风险
  • JavaScript 代码可能被恶意注入,需确保加载内容可信。
  • addJavascriptInterface 在 API 17 以下有安全漏洞。

二、准备工作

  1. 权限声明
    如果加载远程网页,需在 AndroidManifest.xml 中添加网络权限:
   <uses-permission android:name="android.permission.INTERNET" />
  1. 添加 WebView
    在布局文件中添加 WebView
   <!-- res/layout/activity_main.xml -->
   <WebView
       android:id="@+id/webView"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
  1. 启用 JavaScript
    确保 WebView 支持 JavaScript(默认禁用):
   webView.getSettings().setJavaScriptEnabled(true);
  1. 注意
  • 安全性:仅对受信任的网页启用 setJavaScriptEnabled
  • HTTPS:Android 10+ 默认禁用 HTTP,推荐使用 HTTPS 或配置 android:usesCleartextTraffic="true"(不推荐)。
  • API 兼容:API 17+ 推荐使用 evaluateJavascript 替代 loadUrl 执行 JavaScript。

三、WebView 与 JavaScript 交互基本用法

以下是实现 JavaScript 与 Android 双向交互的详细步骤和代码示例。

1. JavaScript 调用 Android 方法

通过 addJavascriptInterface 将 Android 方法暴露给 JavaScript。

  • 步骤
  1. 定义一个带有 @JavascriptInterface 注解的接口类。
  2. 将接口对象绑定到 WebView。
  3. 在 HTML/JavaScript 中调用暴露的方法。
  • 代码示例
  import android.content.Context;
  import android.os.Bundle;
  import android.webkit.JavascriptInterface;
  import android.webkit.WebView;
  import android.widget.Toast;
  import androidx.appcompat.app.AppCompatActivity;

  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.webView);
          webView.getSettings().setJavaScriptEnabled(true); // 启用 JavaScript

          // 绑定 JavaScript 接口
          webView.addJavascriptInterface(new WebAppInterface(this), "Android");

          // 加载本地 HTML
          webView.loadUrl("file:///android_asset/index.html");
      }

      // 定义 JavaScript 接口
      public class WebAppInterface {
          private Context context;

          public WebAppInterface(Context context) {
              this.context = context;
          }

          @JavascriptInterface
          public void showToast(String message) {
              Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
          }

          @JavascriptInterface
          public String getData() {
              return "Data from Android";
          }
      }
  }
  • HTML 文件assets/index.html):
  <!DOCTYPE html>
  <html>
  <head>
      <title>WebView JavaScript</title>
  </head>
  <body>
      <h1>WebView JavaScript Interaction</h1>
      <button onclick="callAndroidToast()">Show Toast</button>
      <button onclick="getAndroidData()">Get Android Data</button>
      <p id="result"></p>

      <script>
          function callAndroidToast() {
              Android.showToast("Hello from JavaScript!");
          }

          function getAndroidData() {
              document.getElementById("result").innerText = Android.getData();
          }
      </script>
  </body>
  </html>
  • 说明
  • addJavascriptInterface(new WebAppInterface(this), "Android"):将接口对象绑定到 JavaScript 的 Android 对象。
  • @JavascriptInterface:标记方法可被 JavaScript 调用。
  • HTML 中的 Android.showToastAndroid.getData 调用 Android 方法。
  • 运行后,点击按钮会显示 Toast 或更新网页内容。

2. Android 调用 JavaScript 方法

通过 WebView.loadUrlevaluateJavascript 执行网页中的 JavaScript 函数。

  • 步骤
  1. 在 HTML 中定义 JavaScript 函数。
  2. 在 Android 代码中调用 webView.loadUrl("javascript:func()")webView.evaluateJavascript
  3. (可选)处理 JavaScript 返回值。
  • 代码示例
  import android.os.Bundle;
  import android.view.View;
  import android.webkit.WebView;
  import android.widget.Button;
  import androidx.appcompat.app.AppCompatActivity;

  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.webView);
          webView.getSettings().setJavaScriptEnabled(true);

          // 加载本地 HTML
          webView.loadUrl("file:///android_asset/index.html");

          // 按钮触发 JavaScript
          Button callJsButton = findViewById(R.id.callJsButton);
          callJsButton.setOnClickListener(v -> {
              // 方法 1: 使用 loadUrl
              webView.loadUrl("javascript:showMessage('Hello from Android!')");

              // 方法 2: 使用 evaluateJavascript (API 19+)
              webView.evaluateJavascript("getDataFromAndroid('Data from Android')", value -> {
                  // 处理返回值
                  Log.d("WebView", "JS Return: " + value);
              });
          });
      }
  }
  • 布局文件res/layout/activity_main.xml):
  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical">
      <Button
          android:id="@+id/callJsButton"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Call JavaScript" />
      <WebView
          android:id="@+id/webView"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
  </LinearLayout>
  • HTML 文件assets/index.html):
  <!DOCTYPE html>
  <html>
  <head>
      <title>WebView JavaScript</title>
  </head>
  <body>
      <h1>WebView JavaScript Interaction</h1>
      <p id="message"></p>

      <script>
          function showMessage(message) {
              document.getElementById("message").innerText = message;
          }

          function getDataFromAndroid(data) {
              return "Received: " + data;
          }
      </script>
  </body>
  </html>
  • 说明
  • loadUrl("javascript:..."):执行 JavaScript 代码,无返回值。
  • evaluateJavascript(API 19+):支持获取返回值,推荐使用。
  • 示例中,点击按钮会调用网页的 showMessage 函数更新内容,或调用 getDataFromAndroid 并获取返回值。

3. 配置 WebViewClient 和 WebChromeClient

为确保交互正常,需配置 WebViewClientWebChromeClient

webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        view.loadUrl(url); // 在 WebView 内加载链接
        return true;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        Log.d("WebView", "Page loaded: " + url);
    }
});

webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        // 处理 JavaScript alert 弹窗
        new AlertDialog.Builder(view.getContext())
                .setMessage(message)
                .setPositiveButton("OK", (dialog, which) -> result.confirm())
                .setCancelable(false)
                .show();
        return true;
    }
});

四、完整代码示例

以下是一个完整的示例,展示 JavaScript 调用 Android 和 Android 调用 JavaScript:

import android.content.Context;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

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.webView);
        webView.getSettings().setJavaScriptEnabled(true);

        // 配置 WebViewClient
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });

        // 配置 WebChromeClient
        webView.setWebChromeClient(new WebChromeClient());

        // 绑定 JavaScript 接口
        webView.addJavascriptInterface(new WebAppInterface(this), "Android");

        // 加载本地 HTML
        webView.loadUrl("file:///android_asset/index.html");

        // 调用 JavaScript
        Button callJsButton = findViewById(R.id.callJsButton);
        callJsButton.setOnClickListener(v -> {
            webView.evaluateJavascript("showMessage('Hello from Android!')", value -> {
                Log.d("WebView", "JS Return: " + value);
            });
        });
    }

    public class WebAppInterface {
        private Context context;

        public WebAppInterface(Context context) {
            this.context = context;
        }

        @JavascriptInterface
        public void showToast(String message) {
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
        }

        @JavascriptInterface
        public String getData() {
            return "Data from Android";
        }
    }
}
  • 布局文件res/layout/activity_main.xml):
  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical">
      <Button
          android:id="@+id/callJsButton"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Call JavaScript" />
      <WebView
          android:id="@+id/webView"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
  </LinearLayout>
  • HTML 文件assets/index.html):
  <!DOCTYPE html>
  <html>
  <head>
      <title>WebView JavaScript</title>
  </head>
  <body>
      <h1>WebView JavaScript Interaction</h1>
      <button onclick="Android.showToast('Hello from JS!')">Show Toast</button>
      <button onclick="showAndroidData()">Get Android Data</button>
      <p id="message"></p>
      <p id="result"></p>

      <script>
          function showMessage(message) {
              document.getElementById("message").innerText = message;
          }

          function showAndroidData() {
              document.getElementById("result").innerText = Android.getData();
          }
      </script>
  </body>
  </html>
  • 运行效果
  • 点击 HTML 按钮,调用 Android 的 showToast 显示 Toast,或显示 Android 返回的数据。
  • 点击 Android 按钮,调用 JavaScript 的 showMessage 更新网页内容。

五、注意事项

  1. 安全问题
  • JavaScript 注入:启用 setJavaScriptEnabled(true) 时,仅加载受信任的网页,避免 XSS 攻击。
  • API 17 以下漏洞addJavascriptInterface 在低版本有安全风险,建议设置 minSdkVersion 为 17 或使用 WebView 安全库。
  • HTTPS:优先使用 HTTPS URL,Android 10+ 默认禁用 HTTP。
  1. 性能优化
  • 避免加载复杂网页,减少内存占用。
  • 启用缓存:
    java webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); webView.getSettings().setAppCacheEnabled(true); webView.getSettings().setAppCachePath(getCacheDir().getAbsolutePath());
  1. 错误处理
  • 处理加载失败:
    java webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { Log.e("WebView", "Error: " + error.getDescription()); } });
  • 检查网络状态:
    java ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = cm.getActiveNetworkInfo(); if (networkInfo == null || !networkInfo.isConnected()) { Toast.makeText(this, "No network", Toast.LENGTH_SHORT).show(); }
  1. 生命周期管理
  • 暂停/恢复 WebView: @Override protected void onPause() { super.onPause(); webView.onPause(); webView.pauseTimers(); } @Override protected void onResume() { super.onResume(); webView.onResume(); webView.resumeTimers(); }
  • 销毁 WebView:
    java @Override protected void onDestroy() { super.onDestroy(); webView.destroy(); }
  1. API 兼容
  • 使用 evaluateJavascript(API 19+)替代 loadUrl
  • 定期更新系统 WebView(通过 Google Play)。

六、学习建议与实践

  1. 学习路径
  • 掌握 addJavascriptInterface 实现 JavaScript 调用 Android。
  • 学习 evaluateJavascript 执行 JavaScript 代码。
  • 理解 WebViewClientWebChromeClient 的作用。
  • 探索复杂交互(如传递复杂数据、处理回调)。
  • 学习混合开发(Hybrid App)架构。
  1. 实践项目
  • 简单项目:实现 JavaScript 调用 Android 显示 Toast。
  • 进阶项目:Android 传递 JSON 数据给 JavaScript,动态更新网页。
  • 高级项目:开发一个简单的 Hybrid App,结合 REST API 和 WebView。
  1. 调试工具
  • Chrome DevTools:通过 chrome://inspect 调试 WebView(需启用开发者模式)。
  • Logcat:查看 JavaScript 和 Android 日志。
  • Charles/Fiddler:抓包分析网络请求。
  1. 推荐资源
  • Android 官方文档:https://developer.android.com/reference/android/webkit/WebView
  • WebView 调试:https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews
  • JavaScript 交互:https://developer.android.com/guide/webapps/webview#BindingJavaScript

七、总结

  • JavaScript 调用 Android:使用 addJavascriptInterface 暴露方法,JavaScript 通过对象(如 Android)调用。
  • Android 调用 JavaScript:使用 loadUrlevaluateJavascript 执行 JavaScript 函数。
  • 核心配置:启用 JavaScript,设置 WebViewClientWebChromeClient
  • 注意事项:安全(避免 XSS、HTTPS)、性能(缓存)、生命周期管理。
  • 推荐:优先使用 evaluateJavascript,仅加载受信任内容。
  • 实践:实现简单交互,逐步开发 Hybrid App。

如果需要更详细的代码示例(如复杂数据交互、Hybrid App 架构)或特定场景的讲解,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注