如何从Android调用javascript?
Posted
技术标签:
【中文标题】如何从Android调用javascript?【英文标题】:How to call javascript from Android? 【发布时间】:2011-11-24 13:43:50 【问题描述】:我们如何从 android 调用 javascript?我有这个我想使用的 javascript 库,我想调用 javascript 函数并将结果值传递给 android java 代码。从现在开始还没有找到答案。我设法从 javascript 调用 android 代码,但我想要相反的方式。
【问题讨论】:
只需在 code.google.com 上查看问题 WebView has no way to call JavaScript from Java 目前的解决方案是加载一个JavaScript URL,例如:webview.loadUrl("javascript:(function() " + "document.getElementsByTagName('body')[0].style。颜色 = '红色'; " + ")()");在某些方面,一旦你看到它,它看起来就非常优雅和明显,但它仍然不如直接执行 JavaScript 的方法那么优雅。然而,这确实表明实现这样的方法很容易。 【参考方案1】:有一个hack:
绑定一些 Java 对象,以便可以通过 WebView 从 Javascript 调用它:
addJavascriptInterface(javaObjectCallback, "JavaCallback")
通过
在现有页面中强制执行javascriptWebView.loadUrl("javascript: var result = window.YourJSLibrary.callSomeFunction();
window.JavaCallback.returnResult(result)");
(在这种情况下,您的 java 类 JavaObjectCallback
应该有一个方法 returnResult(..)
)
注意:这是一个安全风险 - 此网页中的任何 JS 代码都可以访问/调用您绑定的 Java 对象。最好将一些一次性 cookie 传递给 loadUrl() 并将它们传回您的 Java 对象,以检查是您的代码进行调用。
【讨论】:
Android 开发者页面也谈到了这种安全风险。 -- 我想如果JavascriptInterface
只包含可以从JavaScript 访问的方法,这就是为什么在JellyBean (api 17) 中引入@JavascriptInterface 注释的原因?
@peter-knego returnResult(..)
中的参数应该使用什么类型?比如我想返回htmlCollection类型的document.getElementsByClassName('someclass')
,但是Java中没有这种类型?如果我只使用 String 或 Object 作为参数类型,它将返回 null 和“未定义”..
我想知道这对我来说是否有点矫枉过正。我要做的就是在我的网站上解析一个 HTML 表格。我仍在寻找方法来做到这一点。我有一个将表格转换为 json 的 javascript 函数,我只需要一种在 java 中获取它的方法。【参考方案2】:
您可以使用Rhino
库在没有 WebView 的情况下执行 JavaScript。
Download Rhino首先解压,将js.jar文件放到libs文件夹下。它非常小,因此您不必担心您的 apk 文件会因为这个外部 jar 包而变得大得离谱。
这里是一些执行 JavaScript 代码的简单代码。
Object[] params = new Object[] "javaScriptParam" ;
// Every Rhino VM begins with the enter()
// This Context is not Android's Context
Context rhino = Context.enter();
// Turn off optimization to make Rhino Android compatible
rhino.setOptimizationLevel(-1);
try
Scriptable scope = rhino.initStandardObjects();
// Note the forth argument is 1, which means the JavaScript source has
// been compressed to only one line using something like YUI
rhino.evaluateString(scope, javaScriptCode, "JavaScript", 1, null);
// Get the functionName defined in JavaScriptCode
Object obj = scope.get(functionNameInJavaScriptCode, scope);
if (obj instanceof Function)
Function jsFunction = (Function) obj;
// Call the function with params
Object jsResult = jsFunction.call(rhino, scope, scope, params);
// Parse the jsResult object to a String
String result = Context.toString(jsResult);
finally
Context.exit();
您可以在my post查看更多详细信息。
【讨论】:
【参考方案3】:为了匹配ios WebviewJavascriptBridge(https://github.com/marcuswestin/WebViewJavascriptBridge)的方法调用,我为register_handle
和call_handle
的调用做了一些代理。请注意,我不是 Javascript 专家,因此可能有更好的解决方案。
javascriptBridge = (function()
var handlers = ;
return
init: function ()
,
getHandlers : function()
return handlers;
,
callHandler : function(name, param)
if(param !== null && param !== undefined)
JSInterface[name](param);
else
JSInterface[name]();
,
registerHandler : function(name, method)
if(handlers === undefined)
handlers = ;
if(handlers[name] === undefined)
handlers[name] = method;
;
());
通过这种方式,您可以从 Javascript 发送可以具有字符串参数的 Java 调用
javascriptBridge.callHandler("login", JSON.stringify(jsonObj));
调用
@JavascriptInterface
public void login(String credentialsJSON)
Log.d(getClass().getName(), "Login: " + credentialsJSON);
new Thread(new Runnable()
public void run()
Gson gson = new Gson();
LoginObject credentials = gson.fromJson(credentialsJSON, LoginObject.class);
SingletonBus.INSTANCE.getBus().post(new Events.Login.LoginEvent(credentials));
).start();
你可以用
回调JavascriptjavascriptBridge.registerHandler('successfullAuthentication', function ()
alert('hello');
)
和
private Handler webViewHandler = new Handler(Looper.myLooper());
webViewHandler.post(
new Runnable()
public void run()
webView.loadUrl("javascript: javascriptBridge.getHandlers().successfullAuthentication();"
);
如果需要传递参数,序列化为JSON字符串然后调用StringEscapeUtils.escapeEcmaScript(json)
,否则会得到unexpected identifier: source (1)
错误。
有点俗气和hacky,但它有效。您只需删除以下内容。
connectWebViewJavascriptBridge(function(bridge)
编辑:
为了把全局变量改成实际的属性,我把上面的代码改成如下:
(function(root)
root.bridge = (function()
var handlers = ;
return
init: function ()
,
getHandlers : function()
return handlers;
,
callHandler : function(name, param)
if(param !== null && param !== undefined)
Android[name](param);
else
Android[name]();
,
registerHandler : function(name, method)
if(handlers === undefined)
handlers = ;
if(handlers[name] === undefined)
handlers[name] = method;
;
());
)(this);
我的想法来自Javascript global module or global variable。
【讨论】:
【参考方案4】:有关不需要使用慢速 WebView 的 JavaScript 完整实现,请参阅AndroidJSCore,它是适用于 Android 的 Webkit 的 JavaScriptCore 的完整端口。
2018 年更新:AndroidJSCore 已弃用。但是,它的继任者 LiquidCore 具有所有相同的功能,甚至更多。
从 Android 调用函数非常简单:
JSContext context = new JSContext();
String script =
"function factorial(x) var f = 1; for(; x > 1; x--) f *= x; return f; \n" +
"var fact_a = factorial(a);\n";
context.evaluateScript("var a = 10;");
context.evaluateScript(script);
JSValue fact_a = context.property("fact_a");
System.out.println(df.format(fact_a.toNumber())); // 3628800.0
【讨论】:
嗨,埃里克。您还忘了提到它现在已弃用,取而代之的是 LiquidCore。以上是关于如何从Android调用javascript?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Objective-C 中调用的 JavaScript 函数访问 response.body?