web-dev-qa-db-fra.com

Android utilise V8 sans WebView

Je m'exerce à exécuter javascript à partir de Java. Rhino fonctionne très bien pour cela sur le bureau, mais doit revenir au mode interprété (lent) sur Android (dalvik étant incapable d'exécuter le bytecode Java compilé par Rhino JIT).

Android a son moteur javascript V8 intégré auquel on accède en interne via JNI et qui devrait donner de bien meilleures performances que Rhino; Cependant, le seul moyen que je puisse trouver pour y accéder est indirectement par le biais d'une WebView.

Malheureusement, WebView requiert un contexte et se bloque avec NPE avec un contexte nul. Par conséquent, je ne peux même pas instancier une fictive WebView pour exécuter simplement le code et renvoyer le résultat. La nature de mon exercice ne me permet pas vraiment de fournir un contexte à WebView. J'espère donc qu'il y a quelque chose que je néglige.

Plusieurs de ces V8Threads fonctionnent en parallèle, il n'est donc pas vraiment possible (à ma connaissance) d'ajouter une WebView à ma présentation et de la masquer, car je ne pense pas qu'une seule WebView puisse exécuter des fonctions dans plusieurs threads.

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+")");
    }
}

Idéalement, il doit exister un moyen pris en charge d’appeler directement la V8, ou au moins d’exécuter du javascript sans nécessiter une WebView, car il semble une méthode plutôt encombrante et compliquée pour exécuter du code javascript.

METTRE À JOUR

J'ai un peu réarrangé mon code, bien qu'invisible ici, je suis maintenant en train d'instancier les V8Threads sur onPreExecute () des AsyncTasks tout en conservant le reste dans doInBackground (). Le code source est lu précédemment dans le programme, il n'est donc pas relu de manière redondante pour chaque thread.

Parce que maintenant, la V8Thread est instanciée sur le thread d'interface utilisateur, je peux lui transmettre le contexte de la vue actuelle (j'utilise des fragments, je ne peux donc pas simplement lui transmettre "ceci"), afin qu'elle ne se bloque plus.

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");
    }
}

Cependant, lors de l'exécution, logcat crache un par thread de l'erreur "Impossible d'obtenir le viewWidth après la première mise en page" et le code javascript ne s'exécute jamais. Je sais que le fil se déclenche complètement, car le message de journal "Ici" est envoyé. Voici la fonction test () pertinente dans le code js.

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

En fonctionnant correctement, "blah" devrait apparaître quatre fois dans logcat, mais il n'apparaît jamais. Peut-être que mon code source n'est pas lu correctement, mais j'en doute.

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

La seule autre chose que je puisse imaginer, c’est qu’en raison de ces erreurs, la WebView ne parvient jamais réellement à exécuter le javascript.

J'ajouterai également que pi.js ne contient que du javascript, aucun code HTML. Cependant, même si je l’enveloppe dans juste assez de HTML pour qu’elle puisse être qualifiée de page Web, toujours pas de chance.

45
Uejji

Vous pouvez créer un nouveau contexte V8 via son API et l'utiliser pour exécuter votre code JavaScript dans le répertoire https://Android.googlesource.com/platform/external/v8include qui contient deux fichiers d'en-tête C++. Lien vers la bibliothèque libwebcore.so (compilée à partir de https://Android.googlesource.com/platform/external/webkit ) via le NDK, rien de spécial.

v8::Persistent<v8::Context> context = v8::Persistent<v8::Context>::New(v8::Context::New());
context->Enter();

Reportez-vous à https://developers.google.com/v8/get_started qui fonctionnera sous Android. Assurez-vous simplement que le périphérique est livré avec V8 (certains périphériques plus anciens sont livrés avec JSC [JavaScript Core]).

10
soulseekah

Une réponse un peu tardive mais qui pourrait être utile à quiconque trébuche sur cette question. J'ai utilisé la bibliothèque J2V8 qui est un wrapper Java sur le moteur V8 de Google. Cette bibliothèque est livrée avec des fichiers binaires V8 précompilés pour les appareils Android x86 et armv7l. Cela fonctionne parfaitement. Voir ici pour les tutoriels. Gardez simplement à l'esprit que, puisque le V8 pur n'est qu'un moteur Ecmascript, aucun élément DOM n'est disponible.

5
Angelo

J'ai trouvé ce moteur JS Engine conforme à ECMAScript et open source vraiment génial, entièrement écrit en C appelé duktape

Duktape est un moteur Javascript intégrable, axé sur la portabilité et l'encombrement réduit.

Vous devez toujours passer par le secteur ndk-jni, mais c'est assez simple. Incluez simplement les duktape.c et duktape.h de la source distribuable ici (si vous ne voulez pas suivre le processus de construction vous-même) dans le dossier jni, mettez à jour Android.mk et tout le reste.

Voici un exemple d'extrait de code C pour vous aider à démarrer.

#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;
}

Bonne chance!

2
Pawan Kumar

Vous pouvez utiliser le projet AndroidJSCore . Ce n'est pas basé sur V8, mais JavaScriptCore. La version actuelle (2.2+) prend en charge la compilation JIT sur tous les processeurs non nommés MIPS.

UPDATE 2018: AndroidJSCore a été remplacé par LiquidCore , qui est en fait basé sur V8. Non seulement cela inclut le moteur V8, mais tout Node.js est également disponible.

1
Eric Lange

Pouvez-vous obtenir la Context qui est votre Application? Il y a plusieurs façons de le faire.

  1. Appelez getApplication () depuis votre Activity (Application est un enfant de Context)
  2. Appelez getApplicationContent () depuis un Context (Context étant probablement votre Activity)

METTRE &AGRAVE; JOUR

Selon cette Documentation Android , votre code Javascript lié fonctionnera de toute façon dans un processus séparé. Il ne devrait donc pas être nécessaire de le configurer dans sa propre variable Thread.

Du lien:

Remarque: L'objet lié à votre JavaScript s'exécute dans un autre thread et non dans le thread dans lequel il a été construit. (L'objet auquel on se réfère est la classe JavascriptInterface)

1
nicholas.hauschild