Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous JavaScript calls from Android WebView

I'm building hybrid Android app with WebView that communicates with the device with JavaScriptInterface annotation

From the WebView:

webView.addJavascriptInterface(someService, "someService");

The service implementation:

@JavascriptInterface
public void someMethod() {
    //do some business logic..
}

Problem is that from the JavaScript I run it like this:

function callSomeMethod() {
    someService.someMethod()
};

This call is synchronous, and would like something that will run asynchronously like:

function callSomeMethod(callback) {
    someService.someMethod(function(result) {
        if (result == 'success')
            callback();
    })
};

Preferably using promise:

function callSomeMethod() {
    return someService.someMethod()
    //someMethod returns promise
};

Does Android WebView has built in support for running JavaScript code asynchronously?

like image 276
Guy Segal Avatar asked Jun 13 '15 07:06

Guy Segal


People also ask

How do I enable JavaScript on Android WebView?

Enable JavaScript JavaScript is disabled in a WebView by default. You can enable it through the WebSettings attached to your WebView . You can retrieve WebSettings with getSettings() , then enable JavaScript with setJavaScriptEnabled() . WebView myWebView = (WebView) findViewById(R.

How does JavaScript handle asynchronous calls?

JavaScript provides three methods of handling asynchronous code: callbacks, which allow you to provide functions to call once the asynchronous method has finished running; promises, which allow you to chain methods together; and async/await keywords, which are just some syntactic sugar over promises.

Can WebView run JavaScript?

WebView allows you to bind JavaScript code to Android code through an interface. To do this, we must use the addJavaScriptInterface() method, which is passed the class that provides the interface for JS, and the name that will be used to display the instance in JS (for example, “AndroidFunction“).

Is it possible to run an asynchronous code in JavaScript?

JavaScript is a single-threaded, non-blocking, asynchronous, concurrent programming language with lots of flexibility.


1 Answers

That solely depends on you. You just need to return immediately from the injected method, but be able to call JS code when the execution is complete. Something like this (note that it's only a rough sketch):

private WebView mWebView;
private final Object mLock = new Object();
private String mJsCallbackCode;

@JavascriptInterface
public void someMethod(String jsCallbackCode) {
    synchronized (mLock) {
        mJsCallbackCode = jsCallbackCode;
    }
    // Start some business logic asynchronously, and return back here immediately.
    return;
}

public void onBusinessLogicCompleted(bool success) {
    String jsCallbackCode;
    synchronized (mLock) {
        jsCallbackCode = mJsCallbackCode;
    }
    mWebView.loadUrl("javascript:" + jsCallbackCode + "(" + success + ");void(0);");
}

And in JavaScript you use it like this:

function callSomeMethod(callback) {
    window._someMethodCallback = callback;
    someService.someMethod(
        '(function(success){' +
        '    if (success) window._someMethodCallback();' +
        '    delete window._someMethodCallback;' +
        '})'
    );
};    

So the idea is that you pass the JS code you need to be called back as a string (because you can't pass a real JS object). This code will be called in the global context.

Locking in Java is needed because methods called from JS run on a dedicated thread, not on your app's UI thread.

Note that in M preview, an API for postMessage has been added to WebView, enabling to post asynchronous messages between Java and JS code.

like image 105
Mikhail Naganov Avatar answered Sep 19 '22 13:09

Mikhail Naganov