Stopwatch 2 by Flickr user casey.marshall. License: Attribution 2.0 Generic (CC BY 2.0).

Using Custom Titanium Modules for Performance

by Bill Dawson on 23 April 2013

Yet another reason why one might consider creating a custom Titanium module is if a piece of performance-critical code turns out to be faster when run in a module versus in standard Titanium JavaScript.

As an example, I wanted to test out using a custom Titanium Android module for a simple http GET versus doing the same thing in the standard Titanium.Network.HTTPClient. For my test, I chose the Sunlight Foundations “Capitol Words” API. The app is quite simple: it makes 5 successive calls to the capitol words endpoint, searching for “fiscal cliff” as a term. It tracks the elapsed time for each call and totals them all up, then reports the total time at the end. Here’s the simple test function:

function doTest() {
  var testStart, elapsed = 0, iters = 0,
    cw = require("com.billdawson.cw");
    //cw = require("cw");

  function makeRequest() {
    testStart = new Date();

    cw.doRequest(APIKEY, "fiscal cliff", function(e) {
      elapsed += ((new Date()) - testStart);
      iters++;
      if (iters < 5) {
        makeRequest();
      } else {
        log("Total elapsed milliseconds: " + elapsed);
      }
    });
  }

  makeRequest();
}

Note the cw variable, which will either refer to an external module that I created, or to a simple CommonJS module in Resources/ in the application project. The CommonJS module just uses Titanium.Network.HTTPClient as per usual:

exports.doRequest = function(apikey, term, callback) {
  var url = "http://capitolwords.org/api/text.json?apikey=" + apikey + "&term=" + term.replace(" ", "%20"),
    http = Titanium.Network.createHTTPClient();

  http.onload = function() {
    callback({data: http.responseText});
  };
  http.open("GET", url, true);
  http.send();
};

Then here’s the relevant Java code from the external Titanium Android module I created:

@Kroll.method
public void doRequest(final String api_key, final String searchTerm,
    final KrollFunction callback) {

  AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
    @Override
    protected Void doInBackground(Void[] arg0) {
      try {
        String term = searchTerm.replace(" ", "%20");
        URL url = new URL(CW_URL + "?apikey=" + api_key
            + "&phrase=" + term);
        HttpURLConnection conn = (HttpURLConnection) url
            .openConnection();
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(conn.getInputStream()));
        StringBuffer result = new StringBuffer();
        char[] buffer = new char[500 * 1024];
        int numChars = 0;
        while ((numChars = reader.read(buffer, 0, buffer.length)) != -1) {
          result.append(buffer, 0, numChars);
        }
        reader.close();
        conn.disconnect();
        KrollDict data = new KrollDict();
        data.put("data", result.toString());
        callback.call(CwModule.this.getKrollObject(), data);

      } catch (Exception e) {
        Log.e(TAG, "Error doing request", e);
      }
      return null;
    }

  };
  task.execute();
}

I ran the test twice for each module. Each test runs the request 5 times, so the 2 tests together meant 10 timed calls each. Each call using the simple CommonJS module (which uses Titanium.Network.HTTPClient) averaged 2278 ms, while the calls via the custom Titanium Android module averaged 1375 ms each. So the custom module was quite a bit faster.

I haven’t investigated why the calls via the custom module are quicker. Admittedly, I’ve made the module ridiculously simple. For example, if an exception occurs, the callback never gets word of it. By contrast, the code behind HTTPClient is obviously more complex. Additionally, my custom module uses HttpURLConnection versus the Apache HTTP stuff, since Google now recommends that Android apps use the former rather than the latter. (Titanium.Network.HTTPClient uses the Apache stuff for now.) I’ve not read anywhere that HttpURLConnection is faster than the Apache HTTP client; I’m just pointing out that it is another way in which my custom module is different than Titanium.Network.HTTPClient and thus could be a factor.

So while you certainly don’t want to turn your whole Titanium application into a bunch of calls to platform-specific custom modules (thereby defeating the purpose of Titanium!), you might consider trying out an external module as a possible solution to a nagging performance problem.

[Photo credit: “Stopwatch 2″ by Flickr user casey.marshall. License: Attribution 2.0 Generic (CC BY 2.0).]

{ 3 comments… read them below or add one }