Android 在 WebView 中调用 JavaScript 函数

Posted

技术标签:

【中文标题】Android 在 WebView 中调用 JavaScript 函数【英文标题】:Android Calling JavaScript functions in WebView 【发布时间】:2011-05-18 13:26:07 【问题描述】:

我正在尝试调用一些位于 html 页面中的 javascript 函数,该页面在 android webview 中运行。下面的代码尝试执行的操作非常简单 - 从 android 应用程序调用带有测试消息的 javascript 函数,该函数又在通过 toast 显示测试消息的 android 应用程序中调用 java 函数。

javascript 函数如下所示:

function testEcho(message)
     window.JSInterface.doEchoTest(message);

在 WebView 中,我尝试通过以下方式调用 javascript,但没有成功:

myWebView.loadUrl("javascript:testEcho(Hello World!)");
mWebView.loadUrl("javascript:(function ()  " + "testEcho(Hello World!);" + ")()");

我确实在 WebView 上启用了 javascript

myWebView.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
myWebView.addJavascriptInterface(myJSInterface, "JSInterface"); 

这是 Java 类

public class JSInterface

private WebView mAppView;
public JSInterface  (WebView appView) 
        this.mAppView = appView;
    

    public void doEchoTest(String echo)
        Toast toast = Toast.makeText(mAppView.getContext(), echo, Toast.LENGTH_SHORT);
        toast.show();
    

我花了很多时间在谷歌上搜索,看看我可能做错了什么。我发现的所有示例都使用这种方法。有人看到这里有问题吗?

编辑:在 html 中引用和使用了其他几个外部 javascript 文件,它们可能是问题吗?

【问题讨论】:

您尝试使用的代码在 javascript 端缺少引号。 请注意,从 Android 4.2 开始,您需要在您希望通过 JavaScript 接口提供给 WebView 的 Java 方法上使用 @JavascriptInterface 装饰器。 从代码myWebView.loadUrl("javascript:testEcho('Hello World!')"); 我了解到您已经将 html 文件附加到该 web 视图。你能告诉我你是怎么做到的吗? 【参考方案1】:

我发现了问题所在:testEcho() 参数中缺少引号。这就是我接到工作电话的方式:

myWebView.loadUrl("javascript:testEcho('Hello World!')");

【讨论】:

请查看这些链接***.com/questions/9079374/… 很好,但是如何将 Java 对象作为参数传递给 JavaScript 函数? @KovácsImre String.Format("javascript: testEcho('%d', '%d', '%d')", 1, 2, 3);我猜 谢谢,同时我也发现了。 从KitKat开始有evaluateJavascript方法,查看***.com/a/32163655/3063226【参考方案2】:

从 kitkat 开始,使用 evaluateJavascript 方法而不是 loadUrl 来调用 javascript 函数,如下所示

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) 
        webView.evaluateJavascript("enable();", null);
     else 
        webView.loadUrl("javascript:enable();");
    

【讨论】:

为什么要使用 evaluateJavascript() 而不是 loadUrl()? @kami loadUrl() 将重新加载页面并再次调用 onPageFinished() @Zach 那么在前 KitKat 上仍然是一个问题,不是吗? @kami 添加到 Zach 答案中,evaluateJavascript() 将异步运行 JavaScript。所以我们可以避免使用 evaluateJavascript() 阻塞 UI 线程。 loadUrl 也导致当我尝试在输入字段中填写值时出现软件键盘。【参考方案3】:
public void run(final String scriptSrc)  
        webView.post(new Runnable() 
            @Override
            public void run()  
                webView.loadUrl("javascript:" + scriptSrc); 
            
        ); 
    

【讨论】:

如果这不起作用,请执行 new Thread(new Runnaable()....).start()【参考方案4】:

我创建了一个很好的包装器来调用 JavaScript 方法;它还在日志中显示 JavaScript 错误:

private void callJavaScript(String methodName, Object...params)
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("javascript:try");
    stringBuilder.append(methodName);
    stringBuilder.append("(");
    for (int i = 0; i < params.length; i++) 
        Object param = params[i];
        if(param instanceof String)
            stringBuilder.append("'");
            stringBuilder.append(param.toString().replace("'", "\\'"));
            stringBuilder.append("'");
        
        if(i < params.length - 1)
            stringBuilder.append(",");
        
    
    stringBuilder.append(")catch(error)Android.onError(error.message);");
    webView.loadUrl(stringBuilder.toString());

你也需要添加这个:

private class WebViewInterface

    @JavascriptInterface
    public void onError(String error)
        throw new Error(error);
    

并将这个接口添加到你的 webview 中:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");

【讨论】:

这个功能,无论多么好的想法,都有错误的实现。这个调用:final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a); 将屈服于 SyntaxErrorUnexpected token,当您查看生成的 js 调用时,这并不奇怪:javascript:tryalert(,,'abc',,)catch(error)console.error(error.message); 即使是简单的调用callJavaScript(mBrowser, "alert", "abc", "def"); 也会产生错误,因为末尾总是有流氓逗号。 -1 来自我。 @Lukasz'Severiaan'Grela tnx,我解决了这个问题。 P.S 下次不要偷懒,你可以自己解决它... ;) 供将来参考:让我们保持乐观。指出代码中的错误并不懒惰。它仍然是有用的反馈。 我猜如果任何参数中包含 ' 字符,这将不起作用?示例:callJavascript(mBrowser, "can't do it");【参考方案5】:

是的,您有语法错误。如果要在 logcat 中获取 Javascript 错误和打印语句,则必须在 WebChromeClient 中实现 onConsoleMessage(ConsoleMessage cm) 方法。它提供了完整的堆栈跟踪,如 Web 控制台(检查元素)。方法如下。

public boolean onConsoleMessage(ConsoleMessage cm) 
    
        Log.d("Message", cm.message() + " -- From line "
                             + cm.lineNumber() + " of "
                             + cm.sourceId() );
        return true;
    

实施后,您将在 logcat 上收到 Javascript 错误和打印语句 (console.log)。

【讨论】:

我相信是在 WebChromeClient 而不是 WebViewClient。 是的,你是正确的@MichaelLevy。谢谢你指出我的那个错误。现在我已经编辑了我的答案。 谢谢,您的回答正是我所需要的。我还建议人们在 WebChromeClient 中覆盖 onJsAlert()。在调试 webview 中托管的 Javascript 时,javascript 日志记录和警报的组合非常有用。【参考方案6】:

修改@Ilya_Gazman 答案

    private void callJavaScript(WebView view, String methodName, Object...params)
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("javascript:try");
        stringBuilder.append(methodName);
        stringBuilder.append("(");
        String separator = "";
        for (Object param : params)                
            stringBuilder.append(separator);
            separator = ",";
            if(param instanceof String)
                stringBuilder.append("'");
            
                stringBuilder.append(param.toString().replace("'", "\\'"));
            if(param instanceof String)
                stringBuilder.append("'");
            

        
        stringBuilder.append(")catch(error)console.error(error.message);");
        final String call = stringBuilder.toString();
        Log.i(TAG, "callJavaScript: call="+call);


        view.loadUrl(call);
    

将正确创建 JS 调用,例如

callJavaScript(mBrowser, "alert", "abc", "def");
//javascript:tryalert('abc','def')catch(error)console.error(error.message);
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:tryalert(1,true,'abc')catch(error)console.error(error.message);

请注意,对象不会被正确传递 - 但您可以在作为参数传递之前将它们序列化。

我还更改了错误发生的位置,我已将其转移到控制台日志,可供以下人员收听:

    webView.setWebChromeClient(new CustomWebChromeClient());

和客户

class CustomWebChromeClient extends WebChromeClient 
    private static final String TAG = "CustomWebChromeClient";

    @Override
    public boolean onConsoleMessage(ConsoleMessage cm) 
        Log.d(TAG, String.format("%s @ %d: %s", cm.message(),
                cm.lineNumber(), cm.sourceId()));
        return true;
    

【讨论】:

谢谢。此方法会在任何变量中转义 ' 字符吗?换句话说,它适用于:callJavascript(mBrowser, "can't do it"); 吗? 刚刚编辑了答案并将转义添加到 ' 字符【参考方案7】:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_>

    <WebView
        android:id="@+id/webView"
        android:layout_
        android:layout_/>

</RelativeLayout>

MainActivity.java


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.bluapp.androidview.R;

public class WebViewActivity3 extends AppCompatActivity 
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view3);
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl("file:///android_asset/webview1.html");


        webView.setWebViewClient(new WebViewClient()
            public void onPageFinished(WebView view, String weburl)
                webView.loadUrl("javascript:testEcho('Javascript function in webview')");
            
        );
    

资产文件

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>WebView1</title>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
</head>

<body style="background-color:#212121">
<script type="text/javascript">
function testEcho(p1)
document.write(p1);

</script>
</body>

</html>

【讨论】:

【参考方案8】:

这是一个从 WebView 上的资产加载 js 脚本的示例。 将脚本放入文件将有助于阅读更轻松 我在onPageFinished 中加载脚本,因为我需要访问脚本中的一些 DOM 元素(要能够访问它,应该加载它,否则它将为空)。根据脚本的用途,我们可能会提前加载它

assets/myjsfile.js

document.getElementById("abc").innerText = "def"
document.getElementById("abc").onclick = function() 
    document.getElementById("abc").innerText = "abc"

WebViewActivity

webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() 
   
    override fun onPageFinished(view: WebView?, url: String?) 
        super.onPageFinished(view, url)

        val script = readTextFromAsset("myjsfile.js")
        view.loadUrl("javascript: $script")
    


fun readTextFromAsset(context: Context, fileName: String): String 
    return context.assets.open(fileName).bufferedReader().use  it.readText() 

【讨论】:

以上是关于Android 在 WebView 中调用 JavaScript 函数的主要内容,如果未能解决你的问题,请参考以下文章

Android 在 WebView 中调用 JavaScript 函数

用webview做的android客户端怎样调用摄像头拍照

在Android中调用多个WebView时如何控制内存使用?

Android Webview 和Javascript交互,实现Android和JavaScript相互调用

Android-WebView与本地HTML(播放视频)

Webview Android与js交互