0

I'm attempting to create and Android service that performs a task using JavaScript. I came across this post which describes how to run JavaScript code inside of a WebView within a Service using the WindowManager. I am able to create a WebView with an .html and .js file with no problem. It is once I try to pass data from the android .java service to the WebView that I run into an issue.

I have tried doing so in this fashion:

final WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
...
wv = new WebView(this);
wv.setWebViewClient(new WebViewClient());
wv.getSettings().setJavaScriptEnabled(true);
wv.loadUrl("file:///android_asset/www/test.html");
windowManager.addView(wv, params); // params set using method from linked post above
wv.evaluateJavascript("console.log('hello');", null);
wv.loadUrl("javascript:console.log('blah')");

Neither the call to evaluateJavascript() nor loadUrl() appear to have any effect on the WebView (I access the console using the chrome developer tools).

I have tested that in test.html I can add a <script> tag and output text to the console with no issue.

I've also tried calling the functions before adding the view to no avail.

Gabe O'Leary
  • 2,010
  • 4
  • 24
  • 41
  • I would recommend using a JavaScript interpreter for this, such as [duktape](https://github.com/square/duktape-android). – CommonsWare Sep 12 '17 at 23:14
  • @CommonsWare The reason I am not is that my code makes use of various browser capabilities that I would have to re implement were I using something like duktape (as far as I can tell) – Gabe O'Leary Sep 12 '17 at 23:45
  • So what I understand what you want is to load test.html and excute some JS code? – Steven Sep 13 '17 at 06:48
  • @Steven yes I want to either call a function that is defined in a script within test.html or just output something to the console of test.html. I have not been able to accomplish either. – Gabe O'Leary Sep 13 '17 at 15:57
  • Why aren't running that what you want in log or run run in de script file and if you want to work with that result you can catch that console.log in your android – Steven Sep 13 '17 at 15:59
  • @Steven I need to pass data from the service to the script within the web view. I'm not only trying to log something to the console, this is just a preliminary test that shows the JavaScript doesn't seem to be executed in the same environment as the page... – Gabe O'Leary Sep 13 '17 at 16:17

2 Answers2

1

What kind of data you want to pass to Javascript? You could use the WebView.addJavascriptInterface() to "Plant" methods on the HTML document so you can call them from Javascript, invoke in native and return data back to Javascript. Will that help?

oriharel
  • 10,418
  • 14
  • 48
  • 57
  • I essentially want to pass work to be done to the javascript code (perhaps multiple times) from the native code.. It doesn't seem like the cleanest method for the javascript code to have to ask the native code but it's looking like that may be the only option. I was looking at using a CompletableFuture but that would require me targeting <10% of devices active on the play store. – Gabe O'Leary Sep 13 '17 at 18:28
  • 1
    I can tell you what Cordova is doing. I don't like it, but it may help you. If you want trigger JS to do something from Java, you can invoke WebView.setNetworkAvailable(value). this one will either trigger the Javascript's 'online' or 'offline' event (depending on the value). Add a listener in JS to this (document.addEventListener('online')) - in that invoke a method that has access to the JS interface mentioned above. This is essentially how it's implemented in Cordova for Android. yaiks... – oriharel Sep 14 '17 at 09:40
  • I do not like that at all! guess I'll have to acquiesce considering the unconventional nature of what I am trying to do. – Gabe O'Leary Sep 14 '17 at 16:17
0

If you're trying to execute this from a Service, you'll need to post a Runnable on the UI thread. Read the doc for evaluateJavascript()

It explicitly says it must be called on the UI thread. I think you can just do:

wv.post(new Runnable(){
        @Override public void run()
        {
            wv.evaluateJavascript();
        }
    });

Other than that, should you include tags?

a person
  • 986
  • 6
  • 13
  • I haven't been doing Android Development long but from what I understand using the above code would cause `evaluateJavascript()` to be called from some non main thread. I tried using your technique (didn't work) and then tried this way: https://stackoverflow.com/a/6630052/5186877 to call the method from the UI thread but it also doesn't appear to be working. – Gabe O'Leary Sep 12 '17 at 23:58
  • View.post() is a convenience method used specifically to post onto the UI thread. https://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable) I'm not familiar with evaluateJavascript specifically. Seems like you're doing something wrong if you're relying on WebView for anything. – a person Sep 13 '17 at 00:01
  • I just realized you're loading the URL from assets. You could try WebView.loadData("htmlString", "text/html", null); – a person Sep 13 '17 at 00:15
  • create js method in html file and try to calll like this wv.loadUrl("javascript:urMethodName('blah')"); – james Sep 13 '17 at 01:28
  • @aperson you're right, I misunderstood. It looks like when I use WebView.loadData() the code executed using evalJavascript actually runs within the Webview. I guess I can try and load my page into a string and then create the webview that way. Any idea why it works with loadData() and not with loadUrl()? And thanks for all of the suggestions! – Gabe O'Leary Sep 13 '17 at 16:38