2011-09-25 19 views
10

Come chiamiamo javascript da Android? Ho questa libreria javascript che vorrei usare, voglio chiamare la funzione javascript e passare il valore del risultato al codice java android. Non ho trovato la risposta da ora. Sono riuscito a chiamare il codice di Android da javascript, ma voglio il contrario.Come chiamare javascript da Android?

+0

Basta controllare un problema [WebView non ha modo di chiamare JavaScript da Java] (http://code.google.com/p/android/issues/detail?id=742) all'indirizzo code.google.com –

+1

La soluzione per ora è quello di caricare un'URL JavaScript, ad esempio: webview.loadUrl ("javascript: (function() {" + "document.getElementsByTagName ('body') [0] = .style.color 'rosso';" + "})()"); In un certo senso sembra davvero elegante e ovvio una volta che lo si vede, ma è ancora non ancora elegante come un metodo per eseguire direttamente JavaScript. Questo fa dimostrano, tuttavia, che l'implementazione di tale metodo sarebbe facile. –

risposta

18

C'è un hack:

  1. Bind qualche oggetto Java in modo che possa essere chiamato da JavaScript con WebView:

    addJavascriptInterface(javaObjectCallback, "JavaCallback") 
    
  2. Forza eseguire JavaScript all'interno di una pagina esistente da

    WebView.loadUrl("javascript: var result = window.YourJSLibrary.callSomeFunction(); 
        window.JavaCallback.returnResult(result)"); 
    

(in questo caso la classe java JavaObjectCallback dovrebbe avere un metodo returnResult(..))

Nota: questo è un rischio per la sicurezza: qualsiasi codice JS in questa pagina Web potrebbe accedere/chiamare l'oggetto Java associato. È meglio passare alcuni cookie monouso a loadUrl() e passarli indietro dell'oggetto Java per verificare che sia il tuo codice a effettuare la chiamata.

+2

La pagina dello sviluppatore Android parla anche di questo rischio per la sicurezza. - Suppongo che non ci sia alcun rischio se il 'JavascriptInterface' contiene solo metodi che sono pensati per essere raggiungibili da JavaScript, ed è per questo che l'annotazione @JavascriptInterface è stata introdotta in JellyBean (api 17)? – Maarten

+1

@ peter-knego Che tipo dovrei usare per il parametro in 'returnResult (..)'? Ad esempio, voglio restituire 'document.getElementsByClassName ('someclass')' che è di tipo HTMLCollection, ma non esiste tale tipo in Java? Se uso solo String o Object come tipo di parametro, restituirà null e "undefined" .. –

+0

Mi chiedo se questo potrebbe essere eccessivo per me. Tutto quello che voglio fare è analizzare una tabella HTML sul mio sito web. Sto ancora cercando modi per farlo. Ho una funzione javascript che trasforma il tavolo in json, ho solo bisogno di un modo per farlo entrare in java. – James

2

Al fine di corrispondere le chiamate ai metodi della IOS WebviewJavascriptBridge (https://github.com/marcuswestin/WebViewJavascriptBridge), ho fatto alcuni proxy per le chiamate di register_handle e call_handle. Si prega di notare che non sono un Javascript-guru quindi probabilmente c'è una soluzione migliore.

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

In questo modo è possibile inviare da JavaScript a Java chiamate che possono avere un parametro String

javascriptBridge.callHandler("login", JSON.stringify(jsonObj)); 

le chiamate verso il basso a

@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(); 
} 

e si può richiamare per JavaScript con

javascriptBridge.registerHandler('successfullAuthentication', function() { 
    alert('hello'); 
}) 

e

private Handler webViewHandler = new Handler(Looper.myLooper()); 

webViewHandler.post(
     new Runnable() 
     { 
      public void run() 
      { 
       webView.loadUrl("javascript: javascriptBridge.getHandlers().successfullAuthentication();" 
      } 
     } 
); 

Se è necessario passare un parametro, serializzare a stringa JSON quindi chiamare StringEscapeUtils.escapeEcmaScript(json), altrimenti si ottiene unexpected identifier: source (1) errore.

Un po 'appiccicoso e hacky, ma funziona. Devi solo rimuovere il seguente.

connectWebViewJavascriptBridge(function(bridge) { 
} 

EDIT:

al fine di cambiare la variabile globale ad una effettiva proprietà, ho cambiato il codice di cui sopra al seguente:

(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); 

Ho avuto l'idea da Javascript global module or global variable.

2

È possibile utilizzare la libreria Rhino per eseguire JavaScript senza WebView.

Download Rhino prima, decomprimerlo, inserire il js.jar file nella cartella libs. È molto piccolo, quindi non devi preoccuparti che il tuo file apk sarà ridicolmente grande a causa di questo vaso esterno.

Ecco un semplice codice per eseguire il codice 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(); 
} 

È possibile visualizzare ulteriori dettagli allo my post.

1

Per un'implementazione completa di JavaScript che non richiede l'utilizzo di una visualizzazione Web lenta, vedere AndroidJSCore, che è una porta completa di JavaScriptCore di Webkit per Android.

Richiamo funzioni da Android è molto semplice:

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 

La versione 2.0 ha un ingombro molto ridotto.

Problemi correlati