How to call back from java to javascript in cordova

Motivation

I wanted to notify changes of play state from the native java plugin to javascript in my project. I struggled the mission and I finally solved it. Let me share the solution.

The first implementation

First I tried to store the CallbackContext object which is passed as a parameter of APIs like below.

public class MyOwnPlugin extends CordovaPlugin {
    private CallbackContext callback = null;

    // ...

    public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
        // ...

        } else if("setCallback".equals(action)) {
            callback = callbackContext;
            return true;
        }

        // ...
    }

    // ...

    // This method will be fired later.
    public void onEvent(AnEvent event) {
        if(callback != null) {
            try {
                JSONObject parameter = new JSONObject();
                parameter.put("state", event.getState());
                parameter.put("index", event.getIndex());
                callback.success(parameter);
            } catch (JSONException e) {
                Log.e(TAG, e.toString());
            }
        }
    }
}

Next I implemented javascript like below.

var myOwnPlugin = cordova.require('cordova/plugin/my_own_plugin');
myOwnPlugin.setCallback(myCallback);

// ...

function myCallback() {
    console.log("My callback has been fired.");
}

Finally I modified my_own_plugin.js.

cordova.define('cordova/plugin/my_own_plugin', function(require, exports, module) {
    // ...

    MyOwnPlugin.prototype.setCallback = function(onChanged) {
        exec(onChanged, function(err) {
            console.log(err);
        }, 'MyOwnPlugin', 'setCallback', []);
    }

    // ...
}

However logcat showed below and the callback method was not fired at all after the first call.


Attempted to send a second callback for ID: MyOwnPlugIn392702509 Result was: {"state":1,"index":0}

Final implementation

I googled about the problem for a while and tested several times. Finally I found the solution. I changed MyOwnPlugin.java like below.

public class MyOwnPlugin extends CordovaPlugin {

    // This method will be fired later.
    public void onEvent(AnEvent event) {
        if(callback != null) {
            try {
                JSONObject parameter = new JSONObject();
                parameter.put("param1", event.getParam1());
                parameter.put("param2", event.getParam2());
                // callback.success(parameter);
                PluginResult result = new PluginResult(PluginResult.Status.OK, parameter);
                result.setKeepCallback(true);
                callback.sendPluginResult(result);

            } catch (JSONException e) {
                Log.e(TAG, e.toString());
            }
        }
    }
}

This time logcat did not report any problem.

Conclusion

When you want to call a method of javascript back from a native java plugin, implement like below in java.

  • Store a CallbackContext object
  • Execute the CallbackContext object several times later like below
PluginResult result = new PluginResult(PluginResult.Status.OK, parameter);
result.setKeepCallback(true);
callback.sendPluginResult(result);