如何从Android调用javascript?

IT技术 java javascript android return
2021-01-22 15:18:59

我们如何从 Android 调用 javascript?我有我想使用的这个 javascript 库,我想调用 javascript 函数并将结果值传递给 android java 代码。从现在开始一直没有找到答案。我设法从 javascript 调用 android 代码,但我想反过来。

4个回答

有一个黑客:

  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 对象以检查是否是您的代码进行调用。

@peter-knego 中的参数应该使用什么类型returnResult(..)比如我想返回document.getElementsByClassName('someclass')哪个是HTMLCollection类型,但是Java中没有这个类型?如果我只使用 String 或 Object 作为参数类型,它将返回 null 和“未定义”..
2021-03-20 15:18:59
Android 开发人员的页面也谈到了这种安全风险。-- 我认为如果JavascriptInterface只包含可从 JavaScript 访问的方法,则没有风险,这就是在 JellyBean (api 17) 中引入 @JavascriptInterface 注释的原因?
2021-03-25 15:18:59
我想知道这对我来说是否太过分了。我想要做的就是解析我网站上的 HTML 表格。我仍在寻找方法来做到这一点。我有一个将表转换为 json 的 javascript 函数,我只需要一种方法将它放入 java。
2021-03-26 15:18:59

您可以使用Rhino库在没有 WebView 的情况下执行 JavaScript。

先下载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();
}

您可以在我的帖子中查看更多详细信息

为了匹配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 发送到可以具有 String 参数的 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();
}

你可以用

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)出错。

有点俗气和笨拙,但它有效。您只需要删除以下内容。

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 或 global variable得到了这个想法

有关不需要使用慢速 WebView 的 JavaScript 的完整实现,请参阅AndroidJSCore,它是 Webkit 的 JavaScriptCore for Android 的完整端口。

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。
2021-03-19 15:18:59