2011-09-25 74 views
10

我們如何從Android調用JavaScript?我有這個JavaScript庫,我想使用,我想調用JavaScript函數,並將結果值傳遞給Android Java代碼。從現在起還沒有找到答案。我設法從javascript調用android代碼,但我想要其他方式。如何從Android調用JavaScript?

+0

只需在code.google.com上檢查問題[WebView無法從Java調用JavaScript](http://code.google.com/p/android/issues/detail?id=742) –

+1

解決方案現在是加載一個JavaScript URL,例如: webview.loadUrl(「javascript:(function(){」+ 「document.getElementsByTagName('body')[0] .style.color ='red';」 +「})()」); 從某些方面來看,一旦你看到它,它就顯得非常優雅和明顯,但是它的 仍然不如直接執行JavaScript的方法那麼優雅。但是, 表明,實施這種方法很容易。 –

回答

18

有一個hack:

  1. 綁定一些Java對象,以便它可以通過JavaScript調用的WebView:

    addJavascriptInterface(javaObjectCallback, "JavaCallback") 
    
  2. 隊由

    現有頁面內執行的JavaScript
    WebView.loadUrl("javascript: var result = window.YourJSLibrary.callSomeFunction(); 
        window.JavaCallback.returnResult(result)"); 
    

(在這種情況下,你的java類JavaObjectCallback應該有一個方法returnResult(..)

注:這是一個安全風險 - 任何JS代碼在這個網頁可以訪問/調用你綁定的Java對象。最好將一些一次性cookies傳遞給loadUrl()並將它們傳回給Java對象以檢查它是否是您的代碼進行調用。

+2

Android開發人員的頁面也提到了這種安全風險。 - 如果'JavascriptInterface'只包含可以從JavaScript訪問的方法,那麼我認爲沒有風險,這就是爲什麼@JavascriptInterface註釋是在JellyBean(api 17)中引入的? – Maarten

+1

@ peter-knego我應該在'returnResult(..)'中使用什麼類型的參數?例如,我想返回類型爲HTMLCollection的document.getElementsByClassName('someclass')',但是Java中沒有這種類型?如果我只是使用String或Object作爲參數類型,它將返回null和「undefined」。 –

+0

我想知道這是否對我來說可能是矯枉過正的。我想要做的就是在我的網站上解析一個HTML表格。我仍在尋找方法來做到這一點。我有一個JavaScript函數將錶轉換爲json,我只需要一種方法來獲取它在Java中。 – James

2

爲了匹配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)錯誤。

有點俗氣和哈克,但它的作品。你只需要刪除以下內容。

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的想法。

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看到更多詳細信息。

1

要完整實現不需要使用慢速WebView的JavaScript,請參閱AndroidJSCore,這是Webkit的JavaScriptCore for Android的完整端口。

從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 

2.0版有一個非常小的足跡。