I am running javascript with Java. Rhino works very well for this on the desktop, but should return to (slow) interpreted mode on Android (due to the fact that dalvik cannot execute Java bytecode, compiles the Rhino JIT).
Android has a built-in JavaScript V8 engine, accessed through JNI, and it should provide much better performance than Rhino; however, the only way to find access to it is indirectly through WebView.
Unfortunately, WebView requires context and a failure with NPE with zero context, so I cannot even create an instance of a dummy WebView to just execute the code and return the result. The nature of my exercise does not actually allow me to provide context for the WebView, so I hope that maybe I don’t notice something there.
Some of these V8Threads work in parallel, so it is not possible (as far as I know) to add a WebView to my layout and hide it, since I do not think that one WebView can perform functions in several 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+")"); } }
Ideally, there should be some supported way to directly call V8, or at least run javascript without requiring an actual WebView, since it looks like a rather awkward and confusing method to run javascript code.
UPDATE
I changed my code a bit, although it is unclear that now I am creating an instance of V8Threads in AsyncTasks onPreExecute (), saving everything else in doInBackground (). The source code is read earlier in the program, so it is not redundantly re-read for each stream.
Since V8Thread is now created in the user interface thread, I can pass it the current View of the Context (I use fragments, so I can’t just pass it “this”), so it won’t work anymore.
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");
However, when executing, logcat throws one error out of the stream "Unable to get width view after first layout", and javascript code never executes. I know that the thread is fully triggered because the “Here” log message is being sent. Here is the corresponding test () function in the js code.
function test() { V8Thread.setResult("blah"); }
When working correctly, "blah" should appear four times in logcat, but it never appears. Maybe my source code does not read correctly, but I doubt it.
Scanner scan = new Scanner(assetManager.open("pi.js")); while (scan.hasNextLine()) source += scan.nextLine();
The only thing I can imagine is that due to these aforementioned errors, webView can never cope with javascript execution.
I will also add that pi.js only contains javascript, no HTML. However, even when I wrap it with enough HTML to qualify as a web page, I still have no luck.