Android 提供了一个很强大的 webView 控件用来处理 Web 网页, 而在网页中, JavaScript 又是一个很举足轻重的脚本. 本文将介绍如何实现 Java 代码和 JavaScript 代码的相互调用.
如何实现
实现 Java 和 JS 交互十分便捷. 通常只需要以下几步:
WebView 开启 JavaScript 脚本执行
WebView 设置供 JavaScript 调用的交互接口.
客户端和网页端编写调用对方的代码.
示例代码:
Java 代码
- package com.example.javajsinteractiondemo;
- import Android.annotation.SuppressLint;
- import Android.App.Activity;
- import Android.os.Bundle;
- import Android.util.Log;
- import Android.view.Menu;
- import Android.webkit.JavascriptInterface;
- import Android.webkit.WebChromeClient;
- import Android.webkit.WebSettings;
- import Android.webkit.WebView;
- import Android.webkit.WebViewClient;
- import Android.widget.Toast;
- public class MainActivity extends Activity {
- private static final String LOGTAG = "MainActivity";
- @SuppressLint("JavascriptInterface")
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- final WebView myWebView = (WebView) findViewById(R.id.myWebView);
- WebSettings settings = myWebView.getSettings();
- settings.setJavaScriptEnabled(true);
- myWebView.addJavascriptInterface(new JsInteration(), "control");
- myWebView.setWebChromeClient(new WebChromeClient() {});
- myWebView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- testMethod(myWebView);
- }
- });
- myWebView.loadUrl("file:///android_asset/js_java_interaction.html");
- }
- private void testMethod(WebView webView) {
- String call = "javascript:sayHello()";
- call = "javascript:alertMessage(\"" + "content" + "\")";
- call = "javascript:toastMessage(\"" + "content" + "\")";
- call = "javascript:sumToJava(1,2)";
- webView.loadUrl(call);
- }
- public class JsInteration {
- @JavascriptInterface
- public void toastMessage(String message) {
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
- }
- @JavascriptInterface
- public void onSumResult(int result) {
- Log.i(LOGTAG, "onSumResult result=" + result);
- }
- }
- }
前端网页代码
- <HTML>
- <script type="text/javascript">
- function sayHello() {
- alert("Hello")
- }
- function alertMessage(message) {
- alert(message)
- }
- function toastMessage(message) {
- Windows.control.toastMessage(message)
- }
- function sumToJava(number1, number2){
- Windows.control.onSumResult(number1 + number2)
- }
- </script>
- Java-JavaScript Interaction In Android
- </HTML>
调用示例
JS 调用 Java
调用格式为 Windows.jsInterfaceName.methodName(parameterValues) 此例中我们使用的是 control 作为注入接口名称.
- function toastMessage(message) {
- Windows.control.toastMessage(message)
- }
- function sumToJava(number1, number2){
- Windows.control.onSumResult(number1 + number2)
- }
Java 调用 JS
webView 调用 JS 的基本格式为 webView.loadUrl("javascript:methodName(parameterValues)")
调用 JS 无参无返回值函数:
- String call = "javascript:sayHello()";
- webView.loadUrl(call);
调用 JS 有参无返回值函数:
注意对于字符串作为参数值需要进行转义双引号.
- String call = "javascript:alertMessage(\"" + "content" + "\")";
- webView.loadUrl(call);
调用 JS 有参数有返回值的函数:
Android 在 4.4 之前并没有提供直接调用 JS 函数并获取值的方法, 所以在此之前, 常用的思路是 java 调用 JS 方法, JS 方法执行完毕, 再次调用 java 代码将值返回.
1,Java 调用 JS 代码
- String call = "javascript:sumToJava(1,2)";
- webView.loadUrl(call);
2,JS 函数处理, 并将结果通过调用 java 方法返回
- function sumToJava(number1, number2){
- Windows.control.onSumResult(number1 + number2)
- }
3,Java 在回调方法中获取 JS 函数返回值
- @JavascriptInterface
- public void onSumResult(int result) {
- Log.i(LOGTAG, "onSumResult result=" + result);
- }
处理
Android 4.4 之后使用 evaluateJavascript 即可. 这里展示一个简单的交互示例 具有返回值的 JS 方法
- function getGreetings() {
- return 1;
- }
java 代码时用 evaluateJavascript 方法调用
- private void testEvaluateJavascript(WebView webView) {
- webView.evaluateJavascript("getGreetings()", new ValueCallback<String>() {
- @Override
- public void onReceiveValue(String value) {
- Log.i(LOGTAG, "onReceiveValue value=" + value);
- }});
- }
输出结果
I/MainActivity( 1432): onReceiveValue value=1
注意:
上面限定了结果返回结果为 String, 对于简单的类型会尝试转换成字符串返回, 对于复杂的数据类型, 建议以字符串形式的 JSON 返回.
evaluateJavascript 方法必须在 UI 线程 (主线程) 调用, 因此 onReceiveValue 也执行在主线程.
疑问解答
1,Alert 无法弹出:
你应该是没有设置 WebChromeClient, 按照以下代码设置
- myWebView.setWebChromeClient(new WebChromeClient() {
- });
- Uncaught ReferenceError: functionName is not defined
问题出现原因, 网页的 JS 代码没有加载完成, 就调用了 JS 方法. 解决方法是在网页加载完成之后调用 JS 方法
- myWebView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- // 在这里执行你想调用的 JS 函数
- }
- });
- Uncaught TypeError: Object [object Object] has no method
2, 安全限制问题:
如果只在 4.2 版本以上的机器出问题, 那么就是系统处于安全限制的问题了. Android 文档这样说的
Caution: If you've set your targetSdkVersion to 17 or higher, you must add the @JavascriptInterface annotation to any method that you want available your Web page code (the method must also be public). If you do not provide the annotation, then the method will not accessible by your Web page when running on Android 4.2 or higher.
中文大意为
警告: 如果你的程序目标平台是 17 或者是更高, 你必须要在暴露给网页可调用的方法 (这个方法必须是公开的) 加上 @JavascriptInterface 注释. 如果你不这样做的话, 在 4.2 以以后的平台上, 网页无法访问到你的方法.
解决方法:
将 targetSdkVersion 设置成 17 或更高, 引入 @JavascriptInterface 注释
自己创建一个注释接口名字为 @JavascriptInterface, 然后将其引入. 注意这个接口不能混淆. 这种方式不推荐, 大概在 4.4 之后有问题.
注, 创建 @JavascriptInterface 代码
public @interface JavascriptInterface {}
3, 代码混淆问题:
如果在没有混淆的版本运行正常, 在混淆后的版本的代码运行错误, 并提示 Uncaught TypeError: Object [object Object] has no method, 那就是你没有做混淆例外处理. 在混淆文件加入类似这样的代码
- keepattributes *Annotation*
- keepattributes JavascriptInterface
- -keep class com.example.javajsinteractiondemo$JsInteration {
- *;
- }
- All WebView methods must be called on the same thread
过滤日志曾发现过这个问题.
- E/StrictMode( 1546): java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {
- 528712d4
- } called on Looper (JavaBridge, tid 121) {
- 52b6678c
- }, FYI main Looper is Looper (main, tid 1) {
- 528712d4
- })
- E/StrictMode( 1546): at Android.webkit.WebView.checkThread(WebView.java:2063)
- E/StrictMode( 1546): at Android.webkit.WebView.loadUrl(WebView.java:794)
- E/StrictMode( 1546): at com.xxx.xxxx.xxxx.xxxx.xxxxxxx$JavaScriptInterface.onCanGoBackResult(xxxx.java:96)
- E/StrictMode( 1546): at com.Android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
- E/StrictMode( 1546): at com.Android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
- E/StrictMode( 1546): at Android.os.Handler.dispatchMessage(Handler.java:102)
- E/StrictMode( 1546): at Android.os.Looper.loop(Looper.java:136)
- E/StrictMode( 1546): at Android.os.HandlerThread.run(HandlerThread.java:61)
在 JS 调用后的 Java 回调线程并不是主线程. 如打印日志可验证
ThreadInfo=Thread[WebViewCoreThread,5,main]
解决上述的异常, 将 webview 操作放在主线程中即可.
- webView.post(new Runnable() {
- @Override
- public void run() {
- webView.loadUrl(YOUR_URL).
- }
- });
本文来自 JavaScript 教程 https://www.html.cn/web/javascript/ 栏目.
来源: http://www.css88.com/web/javascript/18245.html