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.cduktape.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()(ApplicationContext 的孩子) 从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的主要内容,如果未能解决你的问题,请参考以下文章

Android ndk没有捆绑v8 .a文件符号

Webview没有清除上一页Android

有没有办法让颤动的 webview 使用 android 相机进行文件上传?如何在 webview_flutter 中打开文件选择器?

Android webview html5地理定位

使用 webview 和刷新按钮加载 android

Android:WebView - 没有为会议启用摄像头和麦克风