Android 使用没有 WebView 的 V8
Posted
技术标签:
【中文标题】Android 使用没有 WebView 的 V8【英文标题】:Android utilize V8 without WebView 【发布时间】:2011-10-16 09:12:56 【问题描述】:我正在练习从 Java 执行 javascript。 Rhino 在桌面上工作得很好,但在 android 上必须回退到(慢)解释模式(由于 dalvik 无法执行 Rhino JIT 编译的 Java 字节码)。
Android 有其内置的 V8 javascript 引擎,可通过 JNI 内部访问,并且应该提供比 Rhino 更好的性能;但是,我能找到访问它的唯一方法是通过 WebView 间接访问。
不幸的是,WebView 需要一个上下文,并且在带有空上下文的 NPE 中崩溃,所以我什至无法实例化一个虚拟 WebView 来仅仅执行代码并返回结果。我的练习的性质并没有真正让我为 WebView 提供上下文,所以我希望我可能忽略了一些东西。
其中几个 V8Thread 并行运行,因此(据我所知)将 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,或者至少在不需要实际 WebView 的情况下执行 javascript,因为仅运行 javascript 代码似乎是一种相当笨拙和复杂的方法。
更新
我已经重新安排了我的代码,虽然这里看不到的是,现在我在 AsyncTasks 的 onPreExecute() 上实例化 V8Threads,同时将其他所有内容保留在 doInBackground() 中。源代码是在程序的早期读入的,因此不会为每个线程重复读取。
因为现在 V8Thread 是在 UI 线程上实例化的,所以我可以将当前视图的 Context 传递给它(我正在使用片段,所以我不能只传递“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 会在每个线程中吐出一个错误“Can't get the viewWidth after the first layout”,并且 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 中使其成为网页,仍然没有运气。
【问题讨论】:
你找到解决办法了吗? 可能有点晚了,但是您是否尝试过禁用 Rhino 的优化?请参阅此问答:***.com/questions/3859305/… 有趣的阅读各种可以使用的引擎 - medium.com/tech-quizlet/… 【参考方案1】:您可以通过其 API 创建一个新的 V8 上下文并使用它来执行您的 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();
请参阅https://developers.google.com/v8/get_started,它适用于 Android。只需确保设备实际附带 V8(一些较旧的设备附带 JSC [JavaScript Core])。
【讨论】:
您是否建议我们编译 V8 并将其包含在应用程序中?我在互联网上的任何地方都没有看到有关如何链接和使用设备的 V8 安装的文档。你说没什么特别的,但我不知道从哪里开始......:/ V8 已经在 libwebcore.so 中可用,只需在您的 NDK 代码中使用它。 有人试过这个吗?听起来好得令人难以置信。 当我尝试加载库时,通过:static System.loadLibrary("webcore");
我得到一个 UnsatisfiedLinkError 有没有人通过这里完成的操作成功加载了库:WebViewCore
这真的很需要一个能做到这一点的 Github 示例应用程序。是否可以通过仅包含一个很小的存根代码而不是相当大的完整 v8 二进制文件来使用 v8 javascript 引擎?【参考方案2】:
回复有点晚,但它可能对任何偶然发现这个问题的人有用。我使用了J2V8 库,它是Google V8 引擎上的Java 包装器。该库带有用于 x86 和 armv7l Android 设备的预编译 V8 二进制文件。它无缝地工作。有关教程,请参阅here。请记住,由于纯 V8 只是一个 Ecmascript 引擎,因此没有可用的 DOM 元素。
【讨论】:
【参考方案3】:我发现这个非常漂亮的开源 ECMAScript 兼容 JS 引擎完全用 C 编写,名为 duktape
Duktape 是一个可嵌入的 Javascript 引擎,专注于便携性和紧凑的占用空间。
您仍然需要通过 ndk-jni 业务,但这很简单。只需将可分发源 here(如果您不想自己完成构建过程)中的 duktape.c
和 duktape.h
包含到 jni 文件夹中,更新 Android.mk 和所有这些东西。
这里有一个示例 C sn-p 可以帮助您入门。
#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;
祝你好运!
【讨论】:
【参考方案4】:你能拿到Context
,也就是你的Application
吗?有几种方法可以做到这一点。
-
从您的
Activity
呼叫getApplication()(Application
是Context
的孩子)
从Context
致电getApplicationContent()(Context
可能是您的Activity
)
更新
根据这个Android documentation,你绑定的Javascript代码无论如何都会在一个单独的进程中运行,所以应该不需要在它自己的Thread
中设置它。
来自链接:
注意:绑定到 JavaScript 的对象在另一个线程中运行,而不是在构造它的线程中。 (所指的“对象”是 JavascriptInterface 类)
【讨论】:
如果我尝试为 WebView 提供我的实际上下文,它会崩溃并显示“无法在未调用 Looper.prepare() 的线程内创建处理程序”这似乎与尝试访问以下元素有关在另一个线程的 UI 线程上。 啊,我没注意这是在另一个线程中。如果我有任何建议,我会再看一下并更新我的答案。 另外,我应该补充一点,这些是从 AsyncTask 中调用的,因此会使事情变得更加复杂。 我需要在优化代码时考虑到这一点。然而,经过一番深思熟虑后,我遇到了一个改善情况的想法,但只提出了另一个问题。我会尽快更新我的问题。【参考方案5】:您可以使用AndroidJSCore project。它不是基于 V8,而是 JavaScriptCore。当前版本(2.2+)支持在所有未命名为 MIPS 的处理器上进行 JIT 编译。
2018 年更新: AndroidJSCore 已被LiquidCore 取代,实际上它基于 V8。它不仅包含 V8 引擎,而且所有 Node.js 都可用。
【讨论】:
以上是关于Android 使用没有 WebView 的 V8的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法让颤动的 webview 使用 android 相机进行文件上传?如何在 webview_flutter 中打开文件选择器?