6

I built a very simple PhoneGap Plugin to start testing the way I'm going to build some native actions on Android.

JavaScript:

 function callNativePlugin() {
            cordova.exec(nativePluginResultHandler, nativePluginErrorHandler, "Database", "saveAdvertencia", [ 1, "TesteAdv" ]);
        }

    function nativePluginResultHandler(result) {
        alert("SUCCESS: \r\n" + result);
    }

    function nativePluginErrorHandler(error) {
        alert("ERROR: \r\n" + error);
    }

Java:

@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if (action.equals("saveAdvertencia")) {
        advertenciaDS = new AdvertenciaDS(cordova.getActivity());
        callbackContext.sendPluginResult(new PluginResult(Status.OK, new JSONArray("test")));
        return true;
    }
    return false;
}

What I need is a way to retrieve the result from the action in the same method. It's very complicated to always deal with 3 methods(1 to execute the action. 1 to define what will be the success action. 1 to define what will be the error action.) when you don't really control when they are called, since PhoneGap calls them after the action is completed.

If I need to retrieve some data from the Android Native database:

  1. Call in JavaScript the "cordova.exec".
  2. PhoneGap will call your plugin.
  3. Your plugin will return 2 things: A boolean for the PhoneGap defining it everything worked as expected or not. The data that will be passed to the sucessfull methods.
  4. Here is the tricky part for me. If successfull or not, you have to create 2 methods that you dont have the controll of when they will be called, because PhoneGap will decide that. How do I change this?
BBacon
  • 2,456
  • 5
  • 32
  • 52

3 Answers3

5

I use this pattern to have only one callback for the functions provided by the plugin:

function callNativePlugin(callback) {
   cordova.exec(function(result) {
       callback(null, result);
   }, function(result) {
       callback("myerror");
   }, "Database", "saveAdvertencia", [ 1, "TesteAdv" ])
};

This way you get node.js style callbacks in which the first argument is the error and second is the result:

//define the callback function
var callbackFunction = function(err, result) {
    if (err) {
        console.log("there was an error");
    } else {
        console.log("success", result);
    }
};

//call the plugin
callNativePlugin(callbackFunction);

You cannot avoid having two callbacks in your plugin, as this is mandated by the cordova API, but you don't have to expose that to your plugin users.

Vlad Stirbu
  • 1,732
  • 2
  • 16
  • 27
  • You mean the callNativePlugin? – Vlad Stirbu Mar 22 '13 at 17:51
  • Sorry... I miss understood the code. Already got it working now. – BBacon Mar 22 '13 at 18:17
  • @VladStirbu: I want to handle the `second callback` in phonegap. Can you please tell me how to do this? I have my question [here](http://stackoverflow.com/questions/19177991/) please read [this](http://stackoverflow.com/questions/19177991/asynchronous-communication-between-javascript-and-phonegap-plugin#comment28451880_19198844) comment. Also, I want to know the usage of `sendJavascript()` to call a `JS` method in Java. The question is [here](http://stackoverflow.com/questions/19558535). Please Please Please answer. No one is replying. – Anas Azeem Oct 28 '13 at 10:39
2

If you're asking for a way to turn cordova.exec() from an asynchronous call into a synchronous call that returns a value, the short answer is that you can't. (The long answer is you can fake it using a loop or timeout but you shouldn't.)

As Vlad explains, you can create your own interface so you only have one function to call, but I use something like this simpler version (in http://htmlpresenter.com/api.html):

var Database = {
  saveAdvertencia: function(args, callback) {
    cordova.exec(callback,
      function(error) { console.log("Error calling Database.saveAdvertencia: " + error); },
      "Database", "saveAdvertencia", args)
  }
}

Then call it like this providing the callback inline if you need the result:

Database.Advertencia([1, "TesteAdv"], function(result) { alert(result); });

Of course if (as seems to be the case) you don't need the result at all, you can just use this version:

function saveAdvertencia() {
  log = function(message) { console.log(message) };
  cordova.exec(log, log, "Database", "saveAdvertencia", arguments);
}

saveAdvertencia(1, "TesteAdv");

(Edit:) Also, this challenge may be related to this, better question:

Community
  • 1
  • 1
Dave Burt
  • 1,838
  • 19
  • 20
0

Within JavaScript, I was able to retrieve the PhoneGap plugin value with only one easily controllable recursive method:

function callNativePlugin(result, error, service, action, json) {
    if (result != null) {
        //alert("SUCCESS: \r\n" + result);
        return result;
    } else if (error != null) {
        //alert("ERROR: \r\n" + error);
        return error;
    }
    return cordova.exec(function(result) {callNativePlugin(result, null, null, null);}, function(error) {callNativePlugin(null, error, null, null);}, service, action, json);
}
BBacon
  • 2,456
  • 5
  • 32
  • 52
  • What's the value of this over a set of 3 functions that still don't work? You essentially have one function behaving three ways, and still not working because you can't return a value synchronously from an asynchronous call like cordova.exec() (or at least not that simply and you shouldn't). That is, returning from the callback function does not affect the return value of cordova.exec, and probably occurs after cordova.exec returns. Better just to use it asynchronously as designed: function callback(value) { console.log(value); } cordova.exec(callback, callback, "Foo", "bar", []); – Dave Burt Aug 09 '13 at 04:04
  • This is working and being used on my app. Why demote an aswer that is working? Make your own answer and if it is really better and well explained, I will vote it as the correct one. – BBacon Aug 09 '13 at 14:06
  • When I say it doesn't work, I mean that it doesn't return a result or an error, though it will call the native function. It's just a more complicated version of the one you had in the question. I'll suggest another answer also. – Dave Burt Aug 13 '13 at 03:51
  • This is working. It is returning everything correctly here. You are poorly using the code and accusing it to be wrong. – BBacon Aug 18 '13 at 01:51
  • It might be doing what you want it to do, but it doesn't:1) directly return a value; 2) answer your question by having less than three functions (you have three); 3) answer your question by taking back control over when the callback is executed from Phonegap (they are still callbacks called by Phonegap). It is just a complicated version of your original 3 functions which is harder to read and understand. – Dave Burt Aug 23 '13 at 04:27