Android使用V8而不使用WebView
我正在执行从Java执行JavaScript。 犀牛在桌面上工作得很好,但是在Android上必须回到(慢速)解释模式(由于达尔维克无法执行犀牛JIT编译的Java字节码)。
Android有内置的V8 JavaScript引擎,可以通过JNI内部访问,应该比Rhino有更好的性能; 但是,我可以find访问它的唯一方法是间接通过WebView。
不幸的是,WebView需要一个上下文,并与NPE与空上下文崩溃,所以我甚至无法实例化一个虚拟的WebView只是执行代码并返回结果。 我的练习的本质并不能让我为WebView提供一个Context,所以我希望可能有一些我忽略的东西。
其中有几个V8Threads并行运行,所以在我的布局中添加一个WebView并隐藏它并不可行(因为我不相信一个WebView可以在多个线程中执行函数)。
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+")"); } }
理想情况下,必须有一些支持的方式来直接调用V8,或者至less在不需要实际的WebView的情况下执行JavaScript,因为它似乎是一个相当笨拙和复杂的方法来运行JavaScript代码。
UPDATE
我重新安排了一下我的代码,虽然在这里看不到,现在我正在实例化AsyncTasks的onExExecute()上的V8Threads,同时保持doInBackground()中的一切。 源代码是在程序的前面读取的,所以不会为每个线程重复读取。
因为现在V8Thread是在UI线程上实例化的,所以我可以将它传递给当前视图的上下文(我使用的是片段,所以我不能把它传递给this),所以它不再崩溃。
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"); } }
然而,执行时,logcat吐出每个线程的错误“无法获得第一个布局后的viewWidth”,JavaScript代码永远不会执行。 我知道线程完全触发,因为“Here”日志消息被发送。 这里是js代码中相关的test()函数。
function test() { V8Thread.setResult("blah"); }
正常工作,“blah”应该在logcat中显示四次,但它永远不会显示出来。 可能是我的源代码读取不正确,但我怀疑这一点。
Scanner scan = new Scanner(assetManager.open("pi.js")); while (scan.hasNextLine()) source += scan.nextLine();
唯一可以想象的是,由于上述错误,webView永远不会执行JavaScript。
我还要补充说pi.js只包含javascript,不包含任何HTML。 但是,即使当我把它包装在足够的HTML中,以便它有资格成为一个网页,仍然没有运气。
您可以通过API创build一个新的V8 Context,并使用它来执行JavaScript,查看包含两个C ++头文件的https://android.googlesource.com/platform/external/v8 include
目录。 通过NDK链接到libwebcore.so(从https://android.googlesource.com/platform/external/webkit编译),没什么特别的。;
v8::Persistent<v8::Context> context = v8::Persistent<v8::Context>::New(v8::Context::New()); context->Enter();
请参阅可在Android上使用的https://developers.google.com/v8/get_started 。 只要确保该设备实际上与V8一起(一些较旧的设备与JSC [JavaScript核心]一起交付)。
有点迟到的回应,但任何人都可能对这个问题磕磕碰碰。 我使用了J2V8库,它是Google的V8引擎上的Java包装器。 该库附带x86和armv7l Android设备预编译的V8二进制文件。 它无缝工作。 看到这里的教程。 只要保持在中间,因为纯V8只是一个Ecmascript引擎,没有可用的DOM元素。
我发现这个非常漂亮的开源ECMAScript兼容的JS引擎完全用C语言编写,叫duktape
Duktape是一个可embedded的JavaScript引擎,重点在于可移植性和紧凑的空间。
你仍然需要通过ndk-jni的业务,但是非常简单。 只需在这里包含duktape.c
duktape.h
代码中的duktape.c
和duktape.h
(如果您不想亲自执行构build过程)到jni文件夹中,请更新Android.mk和所有这些东西。
下面是一个示例C代码片段,让你开始。
#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; }
祝你好运!
你能获得一个你的Application
的Context
吗? 有几个方法可以做到这一点。
- 从您的
Activity
调用getApplication() (Application
是Context
的子项) - 从
Context
调用getApplicationContent() (Context
可能是您的Activity
)
UPDATE
根据这个Android文档 ,你绑定的Javascript代码将在一个独立的进程中运行,所以不需要在自己的Thread
设置它。
从链接:
注意:绑定到您的JavaScript的对象运行在另一个线程中,而不是在它所在的线程中运行。 (被引用的“对象”是JavascriptInterface类)
您可以使用AndroidJSCore项目 。 它不是基于V8,而是JavaScriptCore。 目前的版本(2.2+)支持所有非MIPS处理器上的JIT编译。