web-dev-qa-db-ja.com

Android WebViewなしでV8を利用する

私はJavaからJavaScriptを実行しています。 Rhinoはデスクトップではこれで非常にうまく機能しますが、Android(dalvikがJavaを実行できないため)で(低速)インタープリターモードにフォールバックする必要がありますRhino JITがコンパイルするバイトコード)。

Androidには、JNIを介して内部的にアクセスされる組み込みのV8 JavaScriptエンジンがあり、Rhinoよりもはるかに優れたパフォーマンスを提供する必要があります。ただし、それにアクセスするために私が見つけることができる唯一の方法は、WebViewを介して間接的に行うことです。

残念ながら、WebViewにはコンテキストが必要であり、コンテキストがnullのNPEでクラッシュするため、ダミーのWebViewをインスタンス化してコードを実行し、結果を返すことすらできません。私のエクササイズの性質上、WebViewのコンテキストを提供することは実際には許可されていないため、おそらく見落としているものがあると思います。

これらのV8Threadsのいくつかは並行して実行されるため、1つの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コードを実行するだけでは、かなり不格好で複雑な方法のようです。

[〜#〜]更新[〜#〜]

私はコードを少し再配置しましたが、ここでは目に見えないのは、他のすべてをdoInBackground()に保ちながら、AsyncTasksのonPreExecute()でV8Threadsをインスタンス化していることです。ソースコードはプログラムの前半で読み込まれるため、スレッドごとに重複して読み込まれることはありません。

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を取得できません」というエラーのスレッドごとに1つ吐き出し、JavaScriptコードは実行されません。 「こちら」のログメッセージが送信されるので、スレッドが完全に起動することはわかっています。これは、jsコード内の関連するtest()関数です。

function test()
{
V8Thread.setResult("blah");
}

正常に機能している場合、「何とか」はlogcatに4回表示されますが、表示されることはありません。ソースコードが正しく読み取られなかった可能性がありますが、私はそれを疑っています。

Scanner scan = new Scanner(assetManager.open("pi.js"));
while (scan.hasNextLine()) source += scan.nextLine();

私が想像できる他の唯一のことは、これらの前述のエラーのために、webViewが実際にjavascriptを実行することは決してないということです。

また、pi.jsにはJavaScriptのみが含まれ、HTMLは含まれないことも追加します。しかし、それをWebページとして認定するのに十分なHTMLでラップしても、まだ運がありません。

45
Uejji

APIを介して新しいV8コンテキストを作成し、それを使用してJavaScriptを実行できます https://Android.googlesource.com/platform/external/v8includeディレクトリを調べます2つのC++ヘッダーファイルが含まれています。 libwebcore.so( https://Android.googlesource.com/platform/external/webkit )ライブラリからNDKを介してリンクします。特別なことは何もありません。

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 Core]が同梱されています)。

10
soulseekah

少し遅れた回答ですが、この質問に出くわした人には役立つかもしれません。私は J2V8 ライブラリを使用しました。これはJava GoogleのV8エンジンのラッパーです。このライブラリには、x86およびarmv7l用のコンパイル済みV8バイナリが付属していますAndroidデバイス。シームレスに動作します。チュートリアルについては こちら を参照してください。純粋なV8はEcmascriptエンジンにすぎないため、利用できるDOM要素はありません。

5
Angelo

私は完全にCで記述された duktape という、本当に気の利いたオープンソースECMAScript準拠のJSエンジンを見つけました

Duktapeは、移植性とコンパクトなフットプリントに重点を置いた、埋め込み可能なJavascriptエンジンです。

あなたはまだndk-jniビジネスを通過する必要がありますが、それは非常に簡単です。 duktape.cおよびduktape.h配布可能なソースから here (自分でビルドプロセスを実行したくない場合)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;
}

幸運を!

2
Pawan Kumar

AndroidJSCoreプロジェクト を使用できます。 V8ではなくJavaScriptCoreに基づいています。現在のバージョン(2.2以降)は、MIPS以外のすべてのプロセッサでのJITコンパイルをサポートしています。

UPDATE 2018:AndroidJSCoreは LiquidCore に置き換えられました。実際、これはV8に基づいています。 V8エンジンが含まれているだけでなく、すべてのNode.jsも使用できます。

1
Eric Lange

あなたのContextであるApplicationを手に入れられますか?これを行うにはいくつかの方法があります。

  1. Activityから getApplication() を呼び出します(ApplicationContextの子です)
  2. Contextから getApplicationContent() を呼び出します(ContextはおそらくActivityです)

[〜#〜]更新[〜#〜]

これによると Androidドキュメント とにかく、バインドされたJavaScriptコードは別のプロセスで実行されるため、独自のThreadに設定する必要はありません。

リンクから:

注:JavaScriptにバインドされたオブジェクトは、それが作成されたスレッドではなく、別のスレッドで実行されます。 (参照されている「オブジェクト」は、JavascriptInterfaceクラスです)

1