Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cordova: How to write native plugins that can repeatedly invoke a Javascript callback?

When developing Cordova plugins, all of the tutorials I have found go something like this:

File: AwesomePlugin.js

var AwesomePlugin = {
  kungfuGripAction = function(target, successCallback, failureCallback) {
    return cordova.exec(
      successCallback,
      failureCallback,
      'AwesomePluginClass',
      'kungfuGripAction',
      [target]
    );
  }
};

module.exports = AwesomePlugin;

File: AwesomePluginClass.java

@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if (ACTION_KUNGFU_GRIP.equals(action)) {
        JSONObject target = args.getJSONObject(0);
        if (gripTarget(target)) {
            callbackContext.success("Target successfully gripped.");
            return true;
        } else {
            callbackContext.error("Could not grip target.");
            return false;
        }
    }

    Log.d(LOG_TAG, "INVALID ACTION! " + action);
    callbackContext.error("Invalid action: " + action);
    return false;
}

File: clientCode.js

AwesomePlugin.kungfuGripAction(cobraEnemy, function(ok) { }, function(err) { });

In the above code, the callbacks can only be called once and are then disposed. If you attempt to call the .success() or .error() method of the callback context object, it will not work and you will get a log message:

Attempted to send a second callback for ID: AwesomePlugin2982699494<BR>W/CordovaPlugin(976) Result was: "Target successfully gripped."

It seems like it is not possible to write a method with a callback that can be called repeatedly seeing as .success() and .error() are the only documented ways to invoke a callback from within native plugin code. While this is mostly what we want, there are times when we want to have the plugin execute a callback repeatedly. For example:

AwesomePlugin.kungfuGripAction(cobraEnemy, function(ok) {
  // After successful grip, punch repeatedly and update life meter.
  AwesomePlugin.punchRepeatedly(cobraEnemy, function(hits) {
    updateLifeMeter(cobraEnemy, hits);
  }, function(err) { });
}, function(err) { });

AwesomePlugin.punchRepeatedly() above will execute repeatedly (maybe in a separate thread) and call function(hits) with each successful execution. If implemented in the de-facto way (using single-use callbacks), you have to either use a loop (which is bad as it is non-async) or tail-call AwesomePlugin.punchRepeatedly() in the callback (error-prone).

What would be the correct way to implement punchRepeatedly() in native code so that it is able register the callback once and then execute it repeatedly?

like image 622
n.abing Avatar asked Aug 25 '14 09:08

n.abing


People also ask

Can I use Cordova plugins with Ionic native?

However, Ionic Native (and therefore, Cordova plugins) can still be used. Vanilla JavaScript apps, targeting ES2015+ and/or TypeScript, can use either Cordova or Capacitor to build native mobile apps. To use any plugin, import the class from the appropriate package and use its static methods:

What does it mean to build Cordova plugins?

Building Cordova plugins means we are writing some JavaScript to call down to some Native (Obj-c/Swift, Java, etc.) code we also write, and returning the result to our JavaScript. To sum it up: we build a Cordova plugin when we want to do something natively that doesn’t yet have a Web API analog,...

What is Cordova and how does it work?

Similar to Capacitor, Ionic’s own native runtime, Cordova allows developers to access native device features, such as camera, keyboard, and geolocation, using a system of plugins. A plugin is a small amount of add-on code that provides JavaScript interface to native components.

How do I specify the Cordova-related dependencies of a plugin?

Cordova 6.1.0 added support for specifying the Cordova-related dependencies of a plugin as part of the plugin's package.json file. Plugins may list the dependencies for multiple releases to provide guidance to the Cordova CLI when it is selecting the version of a plugin to fetch from npm.


1 Answers

I think, you can use a pluginResult with the keepCallback property set to true.

PluginResult result = new PluginResult(PluginResult.Status.OK, "YOUR_MESSAGE");
// PluginResult result = new PluginResult(PluginResult.Status.ERROR, "YOUR_ERROR_MESSAGE");
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);

You should be able to invoke the callback several times this way.

like image 149
jonas Avatar answered Sep 17 '22 16:09

jonas