Home » Android » Apache Cordova – Workflow

Apache Cordova – Workflow

Overview

This is the second article in two part series about Apache Cordova. Before starting I would recommend reading through Apache Cordova – Principle, to get background on how communication happens between JS layer and Android Native layer. This post will be specific to Workflow in Apache Cordova.
Below are topics which I shall cover

  •  SetUp for development
  •  Cordova-2.1.0.js
  •  Important components in Cordova
  •  Request workflow
  •  Plugin Development

Assumptions

1. SetUp for development

Libraries for development can be downloaded here. Below are important components

1. cordova-2.1.0.js
2. cordova-2.1.0.jar

A good example/tutorial for developing Hello World can be found  here
Code for Apache Cordova can be downloaded  here . I recommend you to go ahead and download it to get better understanding

2. cordova-2.1.0.js

In part 1 of this series  we saw how client interaction was facilitated by html and javascript. In case of Cordova this javascript is cordova-2.1.0.js. It should be placed in assets/www directory. It facilitates communication between client and native layer. If you look at js, there are whole bunch of methods but to get started I would look at “cordova/exec“.  Below is section of function

function androidExec(success, fail, service, action, args) {
    // Set default bridge modes if they have not already been set.
    if (jsToNativeBridgeMode === undefined) {
        androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
    }
    if (nativeToJsBridgeMode === undefined) {
        if (callback.isAvailable()) {
            androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET);
        } else {
            androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING);
        }
    }
    try {
      var callbackId = service + cordova.callbackId++,
          argsJson = JSON.stringify(args),
          result;
      if (success || fail) {
          cordova.callbacks[callbackId] = {success:success, fail:fail};
      }
      if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
          window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
      } else if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT) {
          // Explicit cast to string is required on Android 2.1 to convert from
          // a Java string to a JS string.
          result = '' + _cordovaExec.exec(service, action, callbackId, argsJson);
      } else {
          result = prompt(argsJson, "gap:"+JSON.stringify([service, action, callbackId, true]));
      }
      /////// handle result.........
}
   

Key Points to note

  •  jsToNativeBridgeMode - variable that defines various options from communication between JS and NativePlatform.  Default value is PROMPT
  •  nativeToJsBridgeMode- variable that defines various options for communication between Native and JS.
    •   If application loads html from file ie. (file:// ), then callbackserver/XmlHttpRequest (HANGING_GET) is used
    •   If application loads html via http or any other mode POLLING is used for communication

    values for both variables above  are set when first time androidExec is called

  • androidExec - This is the function where most of action happens
    • jsToNativeBridgeMode  is set to PROMPT- Line 4
    • nativeToJsBridgeMode  is set to HANGING_GET- Line 8
    • calling prompt method with parameters which client has passed - Line 27. As discussed in part 1, this method facilitates communication between JS and Native Layer

     

3. Important Components in PhoneGap

Below is list of important components in phonegap and their overview

  • DroidGap - All application which uses Cordova framework needs to extend this class. It initializes various components and loads configuration
  • CordovaWebView- This facilitates in loading of url , initializing  configurations and various components needed by framework. Below are some
      • CordovaWebViewClient  - facilitates in rendering of html. Has callback methods like onPageStarted, onPageFinished etc.
      • CordovaChromeClient - facilitates with Javascript related functionalities. Has callback methods like onJsConfirm, OnJsPrompt etc
      • PluginManager - Maintains Hashmap of  various plugins and facilicates calling of exec method
      • CallBackServer - This server faciliates in sending asynchronous response to XMLHttpRequest.( Similar to TrimmedDownCallBackServer in part 1).
  • IPlugin - Base interface for all Plugins
  • Plugin - Abstract class which implements IPlugin interface. All custom plugin needs to extend this class and override execute method. In execute method you would provide functionality which interacts with native layer and gets the result.  By default when you develop plugin, communication between JS and Native Layer is asynchronous, but if you want to make it synchronous , there is a method isSynch, which you need to override.

4. Request workflow

To demonstrate request workflow, lets consider an example that determines ConnectionInfo for device. This call happens during initialization of framework

Below is the javascript function which gets executed during start up. Note exec function is called.

      NetworkConnection.prototype.getInfo = function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "NetworkStatus", "getConnectionInfo", []);
};

Sequence diagram for request can be represented as below.

     

     Steps Synchronous communication

  1.  Cordova.exec is called. This function calls prompt method
  2.  CordovaChromeClient.onJSPrompt method is intercepted
  3.  PluginManager.exec is called
  4.  Plugin.execute is called in same thread.
  5.  NetworkManager is called, which calls the native library (sockMan.getActiveNetworkInfo()) and returns PluginResult
  6.  CordovaChromeClient responds to client via JsPromptResult.onConfirm method( Pls refer to Part 1  of this series, section - Intercepting in Android)

Exceptions/Other Scenarios

  1.  Above sequence is applicable only where workflow is synchronous
  2.  In case of Plugins workflow is asynchronous( unless you override it)
  3. In asynchronous below are some changes
    • NativetoJSMode is initialized - refer to section 2 above. This will be HANGING_GET( if loading url from file). It generates XMLHttpRequest
    • Steps 1,2 & 3 are same as steps for synchronous communication)
    • Plugin.execute  called in new Thread
    • PluginManager returns null.
    • CordovaChromeClient  responds with blank message- via JSPromptResult.onconfirm  ( Pls refer to Part 1 , section - Intercepting in Android)
    • Response  to client request ( after calling relevant Native API) is propagated via CallbackServer
    • Response is parsed via XMLHttpRequest.onreadystatechange() function and passed on to client

Essentially Corodova uses either JSPromptResult.onconfirm or Callbackserver  to communicate back to client, depending on nature/scope of request
Below are some sections of code which I have included. It should help you get clarity on above sequence of events

CordovaChromeClient 

 ///////....... do some checks
 try {
      array = new JSONArray(defaultValue.substring(4));
      String service = array.getString(0);
      String action = array.getString(1);
      String callbackId = array.getString(2);
      boolean async = array.getBoolean(3);
      PluginResult r = this.appView.pluginManager.exec(service, action, callbackId, message, async);
      result.confirm(r == null ? "" : r.getJSONString());
    } catch (JSONException e) {
     e.printStackTrace();
    }
    /////.........

PluginManager

...............
.................
   final IPlugin plugin = this.getPlugin(service);
   //final CordovaInterface ctx = this.ctx;
   if (plugin != null) {
      runAsync = async && !plugin.isSynch(action);
      if (runAsync) {
            // Run this on a different thread so that this one can return back to JS
            Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                   // Call execute on the plugin so that it can do it's thing
                   PluginResult cr = plugin.execute(action, args, callbackId);
                   String callbackString = cr.toCallbackString(callbackId);
                   if (callbackString != null) {
                       app.sendJavascript(callbackString);
                   }
                } catch (Exception e) {
                   PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
                   app.sendJavascript(cr.toErrorCallbackString(callbackId));
                }
              }
             });
             thread.start();
             return null;
         } else {
            // Call execute on the plugin so that it can do it's thing
             cr = plugin.execute(action, args, callbackId);
             // If no result to be sent and keeping callback, then no need to sent back to JavaScript
            if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
             return null;
             }
          }
      }
      ......//// some other stuff
        return cr;

Plugin - org.apache.cordova.NetworkManager

 /////.......
public PluginResult execute(String action, JSONArray args, String callbackId) {
       PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
       String result = "Unsupported Operation: " + action;
       if (action.equals("getConnectionInfo")) {
          this.connectionCallbackId = callbackId;
          NetworkInfo info = sockMan.getActiveNetworkInfo();
          PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
          pluginResult.setKeepCallback(true);
          return pluginResult;
       }
       return new PluginResult(status, result);
    }
    public boolean isSynch(String action) {
        return true;
        }

5. Plugin Development

Cordova provides some built-in functionality in terms of integration with native platform. You can get list here. However if your application needs functionality not listed there, you can develop your custom plugin.
Lets consider an example scenario where you want to read all SMS from your inbox . In that case you can write plugin for same
A more detailed step-by-step guide for plugin development can be found  here. Below is a quick overview

  1. Create a class that extends org.apache.cordova.api.Plugin
  2. Override execute method with functionality which you want to develop. ie for example above, it will be to access inbox and get the messages.
  3. Add entry for your plugin in config.xml.  For name attribute, make sure you enter complete name including packages.
  4. Write javascript method which calls cardova.exec. Example below
  5. Call the javascript from your html

SMSReader Plugin section of code


public PluginResult execute(String action, JSONArray args, String callbackId) {
     PluginResult result = null;
     JSONObject messages = new JSONObject();
     if (action.equals("inbox")) {
        try {
            messages = readSMS("inbox");
            Log.d("SMSReadPlugin", "Returning " + messages.toString());
            result = new PluginResult(PluginResult.Status.OK, messages);
        } catch (JSONException jsonEx) {
           Log.d("SMSReadPlugin", "Got JSON Exception "+ jsonEx.getMessage());
           result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
        }
      }
     else if(action.equals("sent")){
        try {
            messages = readSMS("sent");
            Log.d("SMSReadPlugin", "Returning " + messages.toString());
           result = new PluginResult(PluginResult.Status.OK, messages);
         } catch (JSONException jsonEx) {
       Log.d("SMSReadPlugin", "Got JSON Exception "+ jsonEx.getMessage());
       result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
       }
     }
else {
       result = new PluginResult(PluginResult.Status.INVALID_ACTION);
        Log.d("SMSReadPlugin", "Invalid action : "+action+" passed");
  }
  return result;
}

Javascript - section of code

   var SmsPlugin = {
      callNativeFunction : function (success, fail) {
            return cordova.exec(success,
            fail,
            "com.abstractlayers.example.SMSReader",
            "inbox",
        []);
      }
    };

You can download the entire sourcecode for above from here
Above code is based of this example . However the example was based of Cordova-1.5 and I have updated it ( javascript and some minor changes in Java) to suit to Cordova 2.1 .
A complete list of existing plugins can be found  here

That covers two part series in Apache Cordova. Hope the series  was informative .

About these ads

9 Comments

  1. You’ll find some interesting points in time in this article but I do not know if I see all of them center to heart. There’s some validity but I will take hold opinion until I appear into it further. Decent article , thanks and we want far more! Added to FeedBurner as well
    jordan shoes http://watchesccoi.do.pe/2013/10/14/beyonce-lady-gaga-high-heels-dancing-high-heels-range-from-fetish-to-mainstream/

  2. The next time I read a blog, I hope that it doesnt disappoint me as significantly as this one. I mean, I know it was my selection to read, but I essentially thought youd have some thing interesting to say. All I hear is usually a bunch of whining about something that you could fix if you ever werent too busy seeking for attention.
    jordans cheap http://rolexwatchesfwk.22116.n7.nabble.com/An-Write-up-On-23isback-td2.html

  3. Youre so cool! I dont suppose Ive read something like this prior to. So nice to get somebody with some original thoughts on this subject. realy thank you for starting this up. this web site is something that is needed on the web, an individual having a small originality. helpful job for bringing some thing new to the net!
    jordans cheap http://isabelmarantyxw.bloguez.com/isabelmarantyxw/5943640/Air_Jordan_11_Concord_2011_View_Your_Exam_Outcomes_Inside_Moments_Of_Declaration_By_Means_Of_the_Ne

  4. When I originally commented I clicked the -Notify me when new comments are added- checkbox and now each time a comment is added I get 4 emails using the identical comment. Is there any way you possibly can take away me from that service? Thanks!
    jordans shoes http://rolexwatchesfwk.hazblog.com/Primer-blog-b1/Air-Jordans-Basketball-Footwear-That-happen-to-be-Famed-On-Their-very-own-b1-p4.htm

  5. Aw, this was a definitely nice post. In notion I would like to put in writing like this moreover – taking time and actual effort to create a rather good article?- but what can I say?- I procrastinate alot and by no means seem to get something performed.
    jordans for cheap http://cheapairjordanqs.bloguez.com/cheapairjordanqs/5961007/Premium_Vs_Standard_What_Porsche_And_Audi_Service_Authorities_Say#.Uq5i-6zXwwg

  6. Oh my goodness! an awesome write-up dude. Thank you Still I am experiencing issue with ur rss . Don’t know why Unable to subscribe to it. Is there anybody acquiring identical rss issue? Everyone who knows kindly respond. Thnkx
    cheap shoes http://ameblo.jp/watchesccoi/entry-11636691377.html

  7. An intriguing discussion is worth comment. I feel that you simply ought to write even more on this topic, it could not be a taboo subject but frequently consumers aren’t enough to speak on such topics. To the next. Cheers
    wholesale jordans http://ameblo.jp/rolexwatcheszqe/entry-11637930384.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: