javascript - Android utiliza V8 sin WebView
(5)
¿Puede obtener una retención del Context
que es su Application
? Hay un par de maneras de hacer esto.
- Llame a getApplication() desde su
Activity
(laApplication
es un hijo deContext
) - Llame a getApplicationContent() desde un
Context
(elContext
probablemente sea suActivity
)
ACTUALIZAR
De acuerdo con esta documentación de Android , su código de JavaScript enlazado se ejecutará en un proceso separado de todos modos, por lo que no debería haber necesidad de configurarlo en su propio Thread
.
Desde el enlace:
Nota: el objeto que está vinculado a su JavaScript se ejecuta en otro hilo y no en el hilo en el que se construyó. (El ''objeto'' al que se hace referencia es la clase JavascriptInterface)
Estoy ejercitando ejecutando javascript desde Java. Rhino funciona muy bien para esto en el escritorio, pero tiene que recurrir al modo interpretado (lento) en Android (debido a que dalvik no puede ejecutar el bytecode de Java que compila el JIT de Rhino).
Android tiene su motor V8 javascript incorporado al que se accede internamente a través de JNI y debería ofrecer un rendimiento mucho mejor que Rhino; sin embargo, la única forma que puedo encontrar para acceder a él es indirectamente a través de una vista web.
Desafortunadamente, WebView requiere un Contexto y se bloquea con NPE con un contexto nulo, por lo que no puedo ni siquiera instanciar un WebView ficticio para simplemente ejecutar el código y devolver el resultado. La naturaleza de mi ejercicio no me permite realmente proporcionar un Contexto para WebView, así que espero que quizás haya algo que esté pasando por alto.
Varios de estos V8Threads se ejecutan en paralelo, por lo que no es realmente factible (por lo que yo sé) agregar un WebView a mi diseño y ocultarlo, ya que no creo que un solo WebView pueda ejecutar funciones en varios hilos.
private class V8Thread extends Thread
{
private WebView webView;
private String source;
private double pi;
private int i, j;
public V8Thread(int i, int j)
{
pi = 0.0;
this.i = i;
this.j = j;
source = "";
try {
InputStreamReader isReader = new InputStreamReader(assetManager.open("pi.js"));
int blah = isReader.read();
while (blah != -1)
{
source += (char)blah;
blah = isReader.read();
}
webView = new WebView(null);
webView.loadData(source, "text/html", "utf-8");
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "V8Thread");
} catch (IOException e) {
e.printStackTrace();
}
}
public double getResult()
{
return pi;
}
@Override
public void run()
{
webView.loadUrl("javascript:Androidpicalc("+i+","+j+")");
}
}
Lo ideal es que exista alguna forma compatible de llamar directamente a V8, o al menos ejecutar javascript sin requerir una vista web real, ya que parece un método bastante torpe y complicado solo para ejecutar el código javascript.
ACTUALIZAR
He reorganizado un poco mi código, aunque lo que no se ve aquí es que ahora estoy creando una instancia de los V8Threads en el OnPreExecute () de AsyncTasks mientras mantengo todo lo demás en doInBackground (). El código fuente se lee anteriormente en el programa, por lo que no se vuelve a leer de forma redundante para cada subproceso.
Debido a que ahora el V8Thread está instanciado en el subproceso de la interfaz de usuario, puedo pasarle el contexto de la vista actual (estoy usando fragmentos, así que no puedo pasarlo "esto"), por lo que ya no se bloquea.
private class V8Thread extends Thread
{
private WebView webView;
private double pi;
private int i, j;
public V8Thread(int i, int j)
{
pi = 0.0;
this.i = i;
this.j = j;
source = "";
webView = new WebView(v.getContext());
}
@SuppressWarnings("unused")
public void setResult(String in)
{
Log.d("Pi",in);
}
public double getResult()
{
return pi;
}
@Override
public void run()
{
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "V8Thread");
webView.loadData(source, "text/html", "utf-8");
//webView.loadUrl("javascript:Androidpicalc("+i+","+j+")");
webView.loadUrl("javascript:test()");
Log.d("V8Thread","Here");
}
}
Sin embargo, al ejecutar, logcat escupe uno por subproceso del error "No se puede obtener el ancho de vista después del primer diseño" y el código javascript nunca se ejecuta. Sé que el hilo se dispara completamente, porque se envía el mensaje de registro "Aquí". Aquí está la función test () relevante en el código js.
function test()
{
V8Thread.setResult("blah");
}
Trabajando correctamente, "blah" debería aparecer cuatro veces en logcat, pero nunca aparece. Podría ser que mi código fuente se lea incorrectamente, pero lo dudo.
Scanner scan = new Scanner(assetManager.open("pi.js"));
while (scan.hasNextLine()) source += scan.nextLine();
Lo único que puedo imaginar es que debido a estos errores mencionados anteriormente, la vista web nunca llega a ejecutar el javascript.
También agregaré que pi.js solo contiene javascript, no HTML en absoluto. Sin embargo, incluso cuando lo envuelvo en HTML suficiente para que califique como una página web, aún no hay suerte.
Encontré este JS Engine muy ingenioso de código abierto compatible con ECMAScript completamente escrito en C llamado duktape
Duktape es un motor de JavaScript integrable, centrado en la portabilidad y la huella compacta.
Aún tendrías que pasar por el negocio de ndk-jni, pero es bastante sencillo. Solo incluye duktape.c
y duktape.h
de la fuente distribuible here (si no quieres pasar por el proceso de compilación tú mismo) en la carpeta jni, actualiza Android.mk y todo eso.
Aquí hay un fragmento de muestra de C para que comiences.
#include "duktape.h"
JNIEXPORT jstring JNICALL
Java_com_ndktest_MainActivity_evalJS
(JNIEnv * env, jobject obj, jstring input){
duk_context *ctx = duk_create_heap_default();
const char *nativeString = (*env)->GetStringUTFChars(env, input, 0);
duk_push_string(ctx, nativeString);
duk_eval(ctx);
(*env)->ReleaseStringUTFChars(env, input, nativeString);
jstring result = (*env)->NewStringUTF(env, duk_to_string(ctx, -1));
duk_destroy_heap(ctx);
return result;
}
¡Buena suerte!
Puede crear un nuevo Contexto V8 a través de su API y usarlo para ejecutar su JavaScript, consulte el directorio de https://android.googlesource.com/platform/external/v8 que contiene dos archivos de encabezado de C ++. Enlace a la biblioteca libwebcore.so (compilado desde https://android.googlesource.com/platform/external/webkit ) a través del NDK, nada especial.
v8::Persistent<v8::Context> context = v8::Persistent<v8::Context>::New(v8::Context::New());
context->Enter();
Consulte https://developers.google.com/v8/get_started que funcionará en Android. Solo asegúrese de que el dispositivo se envíe realmente con V8 (algunos dispositivos más antiguos se envían con JSC [JavaScript Core]).
Puede utilizar el proyecto AndroidJSCore . No se basa en V8, pero JavaScriptCore. La versión actual (2.2+) admite la compilación JIT en todos los procesadores que no se denominan MIPS.
ACTUALIZACIÓN 2018: AndroidJSCore ha sido reemplazado por LiquidCore , que, de hecho, se basa en V8. No solo incluye el motor V8, sino que también está disponible todo Node.js.
Un poco de una respuesta tardía, pero puede ser útil para cualquiera que encuentre esta pregunta. Utilicé la biblioteca J2V8 que es un contenedor Java en el motor V8 de Google. Esta biblioteca viene con archivos binarios V8 precompilados para dispositivos Android x86 y armv7l. Funciona a la perfección. Vea here para tutoriales. Solo mantenga a la mitad, ya que V8 puro es solo un motor Ecmascript, no hay ningún elemento DOM disponible.