2011-09-25 30 views
10

¿Cómo llamamos javascript desde Android? Tengo esta biblioteca de JavaScript que me gustaría usar, quiero llamar a la función javascript y pasar el valor del resultado al código java de Android. No he encontrado la respuesta a partir de ahora. logré llamar al código de Android desde JavaScript, pero quiero que sea al revés.¿Cómo llamar a JavaScript desde Android?

+0

Solo verifique un problema [WebView no tiene forma de llamar a JavaScript desde Java] (http://code.google.com/p/android/issues/detail?id=742) en code.google.com –

+1

La solución por ahora es cargar una URL de JavaScript, por ejemplo: webview.loadUrl ("javascript: (function() {" + "document.getElementsByTagName ('body') [0] .style.color = 'red';" + "})()"); En cierto modo, parece deslumbrantemente elegante y obvio una vez que lo ves, pero luego es todavía no es tan elegante como un método para ejecutar JavaScript directamente. Esto hace demuestran, sin embargo, que la implementación de dicho método sería fácil. –

Respuesta

18

No es un truco:

  1. Enlazar un objeto Java, por lo que se puede llamar desde Javascript con WebView:

    addJavascriptInterface(javaObjectCallback, "JavaCallback") 
    
  2. Fuerza ejecutar código JavaScript dentro de una página existente por

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

(en este caso su clase java JavaObjectCallback debe tener un método returnResult(..))

Nota: este es un riesgo de seguridad: cualquier código JS en esta página web podría acceder/llamar a su objeto enlazado de Java. Lo mejor es pasar algunas cookies únicas a loadUrl() y pasarles su objeto Java para verificar que sea su código quien realiza la llamada.

+2

La página del desarrollador de Android también habla sobre este riesgo de seguridad. - Supongo que no hay ningún riesgo si el 'JavascriptInterface' contiene solo métodos que están destinados a ser accesibles desde JavaScript, y es por eso que la anotación @JavascriptInterface se introdujo en JellyBean (api 17)? – Maarten

+1

@ peter-knego ¿Qué tipo debo usar para el parámetro en 'returnResult (..)'? Por ejemplo, quiero devolver 'document.getElementsByClassName ('someclass')' que es del tipo HTMLCollection, pero no hay tal tipo en Java? Si solo uso String u Object como tipo de parámetro, devolverá null y "undefined" ... –

+0

Me pregunto si esto podría ser demasiado para mí. Todo lo que quiero hacer es analizar una tabla HTML en mi sitio web. Todavía estoy buscando maneras de hacer esto. Tengo una función de Javascript que convierte la tabla en json, solo necesito una forma de obtenerla dentro de Java. – James

2

Con el fin de que coincida con las llamadas de método de la IOS WebviewJavascriptBridge (https://github.com/marcuswestin/WebViewJavascriptBridge), he hecho algunos proxy para las llamadas de register_handle y call_handle. Tenga en cuenta que no soy un gurú de Javascript, por lo tanto, probablemente haya una mejor solución.

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

De esta forma puede enviar desde Javascript para Java llamadas que pueden tener un parámetro de cadena

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

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

y se puede llamar de nuevo a Javascript con

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

y

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

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

Si tiene que pasar un parámetro, serializar a cadena JSON a continuación, llamar StringEscapeUtils.escapeEcmaScript(json), de lo contrario se obtienen unexpected identifier: source (1) error.

Un poco hortera y hacky, pero funciona. Solo tienes que eliminar lo siguiente.

connectWebViewJavascriptBridge(function(bridge) { 
} 

EDIT:

con el fin de cambiar la variable global a una propiedad real, he cambiado el código anterior a lo siguiente:

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

me ocurrió la idea de Javascript global module or global variable.

2

Puede usar la biblioteca Rhino para ejecutar JavaScript sin WebView.

Download Rhino primero, descomprímalo, coloque el js.archivo jar en la carpeta libs. Es muy pequeño, por lo que no tiene que preocuparse de que su archivo apk sea ridículamente grande debido a este único contenedor externo.

Aquí hay algunos códigos simples para ejecutar código 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(); 
} 

Puede ver más detalles en my post.

1

Para una implementación completa de JavaScript que no requiera el uso de una WebView lenta, consulte AndroidJSCore, que es un puerto completo de JavaScriptCore de Webkit para Android.

las funciones de llamada de Android es muy simple:

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 

versión 2.0 tiene una huella muy pequeña.

Cuestiones relacionadas