Javascript Callback function pass to Android

Jonathan picture Jonathan · Aug 11, 2011 · Viewed 48.2k times · Source

I have a javascript interface implemented in Java that is called by my javascript code that is loaded in the webview.

JS Inside webview:

Android.myFunction(function(data){
    console.log(data);
});

Java:

public class JavaScriptInterface {

    Context context;
    WebView webView;

    JavaScriptInterface(Context c, WebView w) {
        context = c;
        webView = w;
    }

    public void myFunction(String callback) {
        //when I log callback, it is "undefined"
         String someData = "Yay for data";
         String js =
             "javascript:(function() { "
                 + "var callback = " + callback + ";"
                 + "callback('" + someData + "');"
             + "})()";
        webView.loadUrl(js);
    }
}

The string that gets loaded by webview ends up being:

javascript:(function() {var callback = undefined; undefined();})()

I have a few ideas:

a. Build the callback in JS as a string.

b. Call the callback's toString() before passing it to Android.myFunction();

My question is, what is the best way to do this? I would love to be able to just pass objects over to Android and it magically works out. Obviously, this isn't the case. ;) What is the next best way to do it?

Answer

csoltenborn picture csoltenborn · Jan 3, 2013

I had a similar problem: From within a web app, I'd like to use a native Android confirmation dialog. That implies that I have to call back from Android into the Javascript part with the result of the confirmation dialog.

I solved this as follows:

function foo() {
    // user confirmation needed
    var dataString = <encode data into string>;
    MyClient.showConfirmationDialog('myCallBackFunction', dataString, 'A title', 'A message');
}

The code above calls the Android javascript interface (see below). The javascript provides the callback method myCallbackFunction(), the name of which is passed to Android as parameter (along with a data string, a title and a message). The callback function looks as follows:

function myCallbackFunction(dataString, result) {
    var data = <decode data from dataString>;
    if (result) {
        // user has confirmed
    } else {
        // user has denied
    }
}

On the Android side, I first activate the Javascript interface in the Activity's onCreate() method:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    WebView webView = new WebView(this);
    setContentView(webView);
    WebSettings settings = webView.getSettings();
    settings.setJavaScriptEnabled(true);
    webView.addJavascriptInterface(new MyJavascriptInterface(webView), "MyClient");
}

The implementation of MyJavascriptInterface then creates the according Android dialog and passes the result back to javascript:

    WebView webView;

    public MyJavascriptInterface(WebView w) {
         this.webView = w;
    }

    @JavascriptInterface
    public void showConfirmationDialog(final String callbackFunction, final String data, String title,
            String message) {

        Dialog.OnClickListener positiveListener = new Dialog.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                webView.loadUrl("javascript:" + callbackFunction + "('" + data + "', true)");
            }
        };
        Dialog.OnClickListener negativeListener = new Dialog.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                webView.loadUrl("javascript:" + callbackFunction + "('" + data + "', false)");
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle(title).setMessage(message).setPositiveButton("Ok", positiveListener)
                .setNegativeButton("Cancel", negativeListener).setCancelable(false);
        builder.create().show();
    }

Passing the callback function's name to Android allows to use several calls to confirmation dialogs, each of which is equipped with an own function do the actual action. The data string will carry all data needed to perform the action (and can even contain Json-encoded objects).