我正在尝试调用一些位于 android webview 中运行的 html 页面中的 javascript 函数。下面的代码尝试执行的操作非常简单 - 从 android 应用程序调用带有测试消息的 javascript 函数,该函数又在 android 应用程序中调用一个 java 函数,该函数通过 toast 显示测试消息。
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 文件,它们可能是问题吗?
@JavascriptInterface
装饰器,以便通过 JavaScript 接口提供给 WebView。
myWebView.loadUrl("javascript:testEcho('Hello World!')");
,我了解到您已将 html 文件附加到该 web 视图。你能告诉我你是怎么做到的吗?
我弄清楚了问题所在: testEcho() 参数中缺少引号。这就是我接到工作电话的方式:
myWebView.loadUrl("javascript:testEcho('Hello World!')");
从 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();");
}
loadUrl
还导致当我尝试在输入字段中填写值时出现软件键盘。
public void run(final String scriptSrc) {
webView.post(new Runnable() {
@Override
public void run() {
webView.loadUrl("javascript:" + scriptSrc);
}
});
}
我创建了一个很好的包装器来调用 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);
将通过 Unexpected token
屈服于 SyntaxError
,当您查看生成的 js 调用时,这并不奇怪:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", "abc", "def");
也会产生错误,因为末尾总是有恶意逗号。 -1 来自我。
callJavascript(mBrowser, "can't do it");
是的,您有语法错误。如果您想在 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
)。
修改@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:try{alert('abc','def')}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:try{alert(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");
一起使用吗?
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</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>
下面是从 WebView
上的资产加载 js 脚本的示例。
将脚本放入文件将有助于更轻松地阅读我在 onPageFinished
中加载脚本,因为我需要访问脚本内的一些 DOM 元素(以便能够访问它应该被加载,否则它将为空)。取决于脚本的用途,我们可能会提前加载它
资产/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()
}