发布于2026-07-23 阅读(0)
扫一扫,手机访问
先说几个核心判断。Android 调用 JS 代码,主流方案有两种:一种是 WebView 的 loadUrl(),另一种是 evaluateJa vascript()。而 JS 反过来调用 Android 代码,则有三种常见手段:通过 addJa vascriptInterface() 做对象映射,通过 WebViewClient 的 shouldOverrideUrlLoading() 拦截 url,或者通过 WebChromeClient 拦截 JS 对话框方法(onJsAlert、onJsConfirm、onJsPrompt)。
这些方法背后的沟通桥梁,其实就是 WebView 本身。
先准备好 HTML 文件,比如叫 ja vascript.html,放在 assets 目录下。
JS Interaction This is a Web View
然后看 MainActivity.ja va:
package com.example.webviewtest;import android.os.Bundle;import android.view.View;import android.webkit.WebSettings;import android.webkit.WebView;import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle sa vedInstanceState) { super.onCreate(sa vedInstanceState); setContentView(R.layout.activity_main); WebView mWebView = findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); // 允许在 WebView 中执行 JS webSettings.setJa vaScriptEnabled(true); // 载入 url mWebView.loadUrl("file:///android_asset/ja vascript.html"); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 调用 JS 的 callJS() 方法 mWebView.loadUrl("ja vascript:callJS()"); } }); }}
布局文件 activity_main.xml 就不多说了,直接贴出来:
这里的关键点在于:只用 loadUrl() 传入字符串 "ja vascript:callJS()",就能调用 JS 中的 callJS() 方法。但必须注意,这个操作会导致页面刷新,而且拿不到 JS 的返回值。
这个方法跟 loadUrl() 相比,优势很明显:
把上面的 HTML 稍微改一下,让 JS 返回一个字符串:
JS Interaction This is a Web View
MainActivity.ja va 对应修改如下:
package com.example.webviewtest;import android.app.AlertDialog;import android.content.DialogInterface;import android.os.Bundle;import android.view.View;import android.webkit.ValueCallback;import android.webkit.WebSettings;import android.webkit.WebView;import android.widget.Button;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle sa vedInstanceState) { super.onCreate(sa vedInstanceState); setContentView(R.layout.activity_main); WebView mWebView = findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJa vaScriptEnabled(true); mWebView.loadUrl("file:///android_asset/ja vascript.html"); Button button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mWebView.evaluateJa vascript("ja vascript:callJS()", new ValueCallback() { @Override public void onReceiveValue(String value) { // 注意:evaluateJa vascript() 返回的值会被封装成 JSON 格式的字符串 // 如果 JS 返回 "Hello from Ja vaScript!",Android 端会收到 "\"Hello from Ja vaScript!\"" // 所以需要手动去掉引号 String result = value != null ? value.replace("\"", "") : ""; AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this); b.setTitle("Alert"); b.setMessage(result); b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); b.setCancelable(false); b.create().show(); } }); } }); }}
evaluateJa vascript() 有两个参数:第一个是 JS 方法字符串,第二个是回调接口,用于接收 JS 执行的返回值。
实际开发中,建议在 Android 4.4 以下用 loadUrl(),4.4 及以上用 evaluateJa vascript()。可以这样判断:
final int version = Build.VERSION.SDK_INT;if (version < Build.VERSION_CODES.KITKAT) { mWebView.loadUrl("ja vascript:callJS()"); } else { mWebView.evaluateJa vascript("ja vascript:callJS()", new ValueCallback() { @Override public void onReceiveValue(String value) { } });}
还有一点:最好在 WebView 页面加载完成之后,再调用 JS 代码,比如在 WebViewClient 的 onPageFinished() 方法中执行。
| 调用方式 | 优点 | 缺点 | 使用建议 |
|---|---|---|---|
| loadUrl() | 简单 | 执行效率低,获取返回值麻烦 | Android 4.4 以下用这个方法 |
| evaluateJa vascript() | 执行效率高 | 向下兼容性差(Android 4.4 以上可用) | Android 4.4 以上用这个方法 |
步骤一:定义一个类,注入到 JS 中。
package com.example.webviewtest;import android.webkit.Ja vascriptInterface;public class JsObject { // 注意:Android 4.2 及以上版本,只有添加了 @Ja vascriptInterface 注解的 public 方法才能被 JS 访问 @Ja vascriptInterface public void hello(String msg) { System.out.println(" JS 调用了 Android 的 hello 方法"); }}
步骤二:在 assets 目录下创建 ja vascript.html。
JS Interaction
步骤三:在 Android 中通过 addJa vascriptInterface() 方法将 Ja va 对象注入 WebView 的 Ja vaScript 环境。
package com.example.webviewtest;import android.os.Bundle;import android.webkit.WebSettings;import android.webkit.WebView;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { WebView mWebView; @Override protected void onCreate(Bundle sa vedInstanceState) { super.onCreate(sa vedInstanceState); setContentView(R.layout.activity_main); mWebView = findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJa vaScriptEnabled(true); // 参数1:注入的 Ja va 对象 // 参数2:暴露在 JS 中的实例名 mWebView.addJa vascriptInterface(new JsObject(), "test"); mWebView.loadUrl("file:///android_asset/ja vascript.html"); }}
布局文件:
这样,点击 HTML 中的 button 就会触发 callAndroid(),进而调用 test 实例的 hello() 方法。这个方案简单易用,但有一个历史遗留问题:在 Android 4.2 以下版本存在安全漏洞。Google 从 Android 4.2 开始要求被调用的方法必须用 @Ja vascriptInterface 注解来防范漏洞攻击,但在 4.2 以下版本这个注解无效,仍然存在风险。如果需要在低版本上使用,通常需要配合拦截 prompt() 的方式进行漏洞修复。
ja vascript.html 内容如下:
JS Interaction
MainActivity.ja va:
package com.example.webviewtest;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.util.Log;import android.webkit.WebResourceRequest;import android.webkit.WebSettings;import android.webkit.WebView;import android.webkit.WebViewClient;import androidx.appcompat.app.AppCompatActivity;import ja va.util.Set;public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle sa vedInstanceState) { super.onCreate(sa vedInstanceState); setContentView(R.layout.activity_main); WebView mWebView = findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJa vaScriptEnabled(true); mWebView.loadUrl("file:///android_asset/ja vascript.html"); mWebView.setWebViewClient(new WebViewClient() { // 旧重载方法在 Android 7.0 以上已废弃 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { // 传入进来的 url = "js://webview?arg1=111&arg2=222" Uri uri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { uri = request.getUrl(); } else { uri = Uri.parse(request.toString()); } // 先判断 scheme if ("js".equals(uri.getScheme())) { // 再判断 authority if ("webview".equals(uri.getAuthority())) { // 执行对应的代码 Log.d(TAG, "JS 调用了 Android 的方法"); Set sets = uri.getQueryParameterNames(); for (String key : sets) { Log.d(TAG, "param:" + uri.getQueryParameter(key)); } return true; } } return super.shouldOverrideUrlLoading(view, request); } }); }}
核心原理就是通过解析 URL 的协议来执行相应的 Android 代码。这个方案没有 addJa vascriptInterface() 的安全漏洞,但如果 JS 想获取 Android 方法的返回值,就比较麻烦了——只能通过 WebView 的 loadUrl() 去调用 JS 中的方法,把返回值传过去。
// Android:mWebView.loadUrl("ja vascript:returnResult(" + result + ")");// JS:ja vascript.htmlfunction returnResult(result){ alert("result is" + result);}
JS 中三个常用的对话框方法:
这三个方法分别会触发 WebChromeClient 的 onJsAlert()、onJsConfirm()、onJsPrompt() 方法。最常用的是 prompt(),因为只有它能返回任意类型的值。看一个例子:
ja vascript.html:
JS Interactiion
MainActivity.ja va:
package com.example.webviewtest;import android.net.Uri;import android.os.Bundle;import android.util.Log;import android.webkit.JsPromptResult;import android.webkit.WebChromeClient;import android.webkit.WebSettings;import android.webkit.WebView;import androidx.appcompat.app.AppCompatActivity;import ja va.util.Set;public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle sa vedInstanceState) { super.onCreate(sa vedInstanceState); setContentView(R.layout.activity_main); WebView mWebView = findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); webSettings.setJa vaScriptEnabled(true); mWebView.loadUrl("file:///android_asset/ja vascript.html"); mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { // 传入进来的 url = "js://webview?arg1=8&arg2=9" Uri uri = Uri.parse(message); if (uri.getScheme().equals("js")) { if (uri.getAuthority().equals("webview")) { // 执行对应的代码 Log.d(TAG, "JS 调用了 Android 的方法"); Set sets = uri.getQueryParameterNames(); for (String key : sets) { Log.d(TAG, "param:" + uri.getQueryParameter(key)); } // JsPromptResult 用于把结果返回给 JS result.confirm("JS 调用 Android 的方法成功啦!"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result); } }); }}
| 调用方式 | 优点 | 缺点 | 使用场景 |
|---|---|---|---|
| 通过 WebView 的 addJa vascriptInterface() 进行对象映射 | 简单 | Android 4.2 以下存在漏洞 | Android 4.2 以上用这个方法 |
| 通过 WebViewClient 的 shouldOverrideUrlLoading() 方法拦截 url | 没有漏洞问题 | 相对复杂,需要解析 url | 不需要 Android 端的方法的返回值的场景 |
| 通过 WebChromeClient 的 onJsAlert()、onJsConfirm()、onJsPrompt() 方法回调拦截 | 没有漏洞问题 | 相对复杂,需要解析 url | 能满足大多数场景 |
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8