Подтвердить что ты не робот

Android использует V8 без WebView

Я выполняю javascript с Java. Rhino работает очень хорошо для этого на рабочем столе, но должен вернуться к (медленному) интерпретируемому режиму на Android (из-за того, что dalvik не может выполнить байт-код Java, компилирует Rhino JIT).

Android имеет встроенный движок JavaScript V8, доступ к которому осуществляется через JNI, и он должен обеспечивать гораздо лучшую производительность, чем Rhino; однако единственный способ найти доступ к нему - косвенно через WebView.

К сожалению, WebView требует контекста и сбой с NPE с нулевым контекстом, поэтому я не могу даже создать экземпляр фиктивного WebView, чтобы просто выполнить код и вернуть результат. Природа моего упражнения на самом деле не позволяет мне предоставлять контекст для WebView, поэтому я надеюсь, что, возможно, там что-то не замечаю.

Некоторые из этих 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 или, по крайней мере, выполнить javascript, не требуя фактического WebView, поскольку он выглядит довольно неуклюжим и запутанным методом для запуска javascript-кода.

UPDATE

Я немного изменил свой код, хотя неясно, что теперь я создаю экземпляр V8Threads в AsyncTasks onPreExecute(), сохраняя все остальное в doInBackground(). Исходный код читается ранее в программе, поэтому он не избыточно перечитывается для каждого потока.

Поскольку теперь V8Thread создается в потоке пользовательского интерфейса, я могу передать ему текущее представление Контекст (я использую фрагменты, поэтому я не могу просто передать его "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 вырывает один на поток ошибки "Невозможно получить вид ширины после первого макета", а код javascript никогда не выполняется. Я знаю, что поток полностью срабатывает, потому что отправляется сообщение журнала "Здесь". Здесь соответствующая функция test() в js-коде.

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, чтобы он квалифицировался как веб-страница, до сих пор не повезло.

4b9b3361

Ответ 1

Вы можете создать новый контекст V8 через свой API и использовать его для выполнения JavaScript, загляните в каталог https://android.googlesource.com/platform/external/v8 include, который содержит два файла заголовков С++, Ссылка на 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();

Обратитесь к https://developers.google.com/v8/get_started, который будет работать на Android. Просто убедитесь, что устройство действительно поставляется с V8 (некоторые более старые устройства поставляются с АО [JavaScript Core]).

Ответ 2

Немного позднего ответа, но может быть полезно любому, кто споткнется на этот вопрос. Я использовал библиотеку J2V8, которая является оболочкой Java на движке Google V8. Эта библиотека поставляется с предварительно скомпилированными двоичными файлами V8 для Android-устройств x86 и armv7l. Он работает без проблем. См. здесь для учебных пособий. Просто держите в середине, что, поскольку чистый V8 - всего лишь движок Ecmascript, нет доступного элемента DOM.

Ответ 3

Я нашел этот действительно отличный open source совместимый с ECMAScript JS Engine, полностью написанный на C под названием duktape

Duktape - это встраиваемый механизм Javascript с акцентом на переносимость и компактность.

Вам все равно придется заниматься бизнесом ndk-jni, но это довольно прямолинейно. Просто включите duktape.c и duktape.h из распространяемого источника здесь (Если вы не хотите самостоятельно проходить процесс сборки) в папку 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;
}

Удачи!

Ответ 4

Можете ли вы удержать Context, который является вашим Application? Есть несколько способов сделать это.

  • Вызов getApplication() из вашего Activity (Application является дочерним элементом Context)
  • Вызов getApplicationContent() из Context (Context скорее всего будет вашим Activity)

UPDATE

В соответствии с этой Android-документацией ваш связанный код Javascript будет работать в отдельном процессе, так что не нужно его настраивать в своем собственном Thread.

Из ссылки:

Примечание. Объект, связанный с вашим JavaScript, выполняется в другом потоке, а не в потоке, в котором он был создан. ( "Объект" относится к классу JavascriptInterface)