【问题标题】:How to call javascript from Android?如何从Android调用javascript?
【发布时间】: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 的方法那么优雅。然而,这确实表明实现这样的方法很容易。

标签: java javascript android return


【解决方案1】:

有一个hack:

  1. 绑定一些 Java 对象,以便可以通过 WebView 从 Javascript 调用它:

    addJavascriptInterface(javaObjectCallback, "JavaCallback")
    
  2. 通过

    在现有页面中强制执行javascript
    WebView.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_handlecall_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();
    }
    

    你可以用

    回调Javascript
    javascriptBridge.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。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多