PhoneGap Native Plugins

UPDATE:

In PhoneGap 1.5, the naming conventions were changed to use the Apache Cordova naming conventions.   EX: PGPlugin is now CDVPlugin, phonegap.js is now cordova.js, etc…   If you are running into issues, please be sure to check the PhoneGap version and appropriate naming conventions.

ORIGINAL POST:

Last night I attended a great Baltimore JavaScript Users Group meetup to engage with the community and to speak about PhoneGap native plugins. Many thanks to @bmorejs for having me, and great job to my fellow presenters Mike Wolf, who presented “PhoneGap: Blood and Guts” and Mike McKay, who presented on PhoneGap + CouchDB.

As promised, here are my slides, and I’ll also try to provide a bit more detail since there isn’t much of an explanation within the slide content: PhoneGap Native Plugins.pdf

PhoneGap Native Plugins Presentation

Here’s a brief synopsis of my presentation…

PhoneGap allows you to create natively installed mobile applications using web technologies (HTML, JS, CSS).  PhoneGap provides an API that lets you access capabilities of the native device/underlying operating system.  “Out of the box”, PhoneGap provides APIs for Accelerometer, Camera, Compass, Media, FileSystem, etc…  (You can get more info about the PhoneGap API from docs.phonegap.com.)

However, PhoneGap does not attempt to recreate every native API.   Luckily, if you need to access native functionality that isn’t already exposed, then you can easily create a native plugin to provide access to that native functionality.   For example, low latency audio processing, multi-screen iOS experiences, or anything else that has a native API.

PhoneGap native plugins shouldn’t be thought of as “plugins” like Flash Player inside of a browser, rather you are “plugging in” additional functionality that extends the core PhoneGap framework.   Even the PhoneGap core API itself is built upon this plugin model.  If you examine PhoneGap.plist in an iOS project, or phonegap.xml in an Android project, you can easily see the API mappings to native classes.

All PhoneGap plugins consist of a JavaScript class that exposes your functionality to your HTML/JS applications (red box in figure below).   The JavaScript class (green box in figure below) communicates to the native layer through the core PhoneGap class using PhoneGap.exec().  This invokes native functionality in your native plugin  (purple box in figure below).

Original image source: wiki.phonegap.com

You develop your JavaScript class to mirror the API of the native class.   In the JavaScript class, you invoke the native function using PhoneGap.exec, as shown below:

PhoneGap.exec(success, fail,   ”NameOfClassMapping”,   ”nameOfFunction", [params]);

Where “success” is a reference to a JavaScript function that will be invoked upon a “success” callback from the native code,  “fail” is a reference to a JavaScript function that will be invoked upon a “fail/error” callback from the native code, “NameOfClassMapping” is the plugin class reference (from phonegap.plist or phonegap.xml), “nameOfFunction” is the name of the native function that should be invoked, and [params] is an array of parameters to be passed into the native function.

In both Android and iOS, native plugin classes extend the PGPlugin class.

iOS

In iOS applications, this function is invoked directly, so invoking a function “myFunction” on the class “MyPlugin” is basically the same as calling the Objective C syntax:

[MyPlugin myFunction];

In iOS applications, you “return” a result to the web view using the writeJavascript method of the PGPlugin class, which invokes a JavaScript string inside of the web view. You create a PluginResult class instance, and write the JavaScript string back to the web view:

PluginResult* pluginResult = [PluginResult resultWithStatus:PGCommandStatus_OK messageAsString: @"OK"];
[self writeJavascript: [pluginResult toSuccessCallbackString:callbackID]];

You can also write native-code-generated JavaScript strings back to the PhoneGap web view using the writeJavaScript method – you can use this to “push” data or instructions to the web view at any time without being requested. An example of this is below:

NSString *js = [NSString stringWithFormat:@"myJSFunction( '%@' );" val];
[self writeJavascript:js];

In the web view, this would invoke the “myJSFunction” JavaScript function with the parameter in the “val” variable.

Android

In Android applications, functions are not invoked directly, instead, the “execute” method of the PGPlugin class in executed.  Inside of your class, you override the “execute” method, and determine the behavior based upon the “action” parameter, as shown below:

public PluginResult execute(String action,   JSONArray data, String callbackId){
  //do something based upon "action"
  if ( action.equals( "foo" ) ) {
     //handle the "foo" action
  }
  else if ( action.equals( "myFunction" ) ) {
     //handle the "myFunction" action
  }
  result = new PluginResult(Status.OK);
  return result;
}

When you return the PluginResult class at the end of this function, that PluginResult will be evaluated to call back to the success or error handler function.

You can also communicate back to the PhoneGap web view using the PGPlugin sendJavascript function to invoke custom JavaScript at any time, just as I described the iOS writeJavascript function above:

this.sendJavascript( myJavaScriptString )

It is important to understand that regardless of iOS, Android, or any other platform, communication between the web view JavaScript and the native code layer is not synchronous. Do not expect that your native code has executed in sequence after you have requested a native command. You need to use the success/error callback paradigm, and build your application logic using asynchronous coding practices.

Also keep in mind that writeJavascript or sendJavascript can be invoked at any time, not just after a method has been invoked by the JavaScript interface. Using sendJavascript and writeJavascript can enable you to “push” data to the web view. For example, let’s say you are monitoring a specific piece of hardware attached to a mobile device … perhaps a heart rate monitor. You could have a native heart rate monitor class that communicates with the device in a separate thread, and it pushes data back to the JavaScript layer in real time.

You can read more specifics about native plugins at http://wiki.phonegap.com

You can read more about the native plugin examples from last night’s presentation at the links below. Both are fully open source and free.

You can also browse the existing collection of open source native plugins at:

Update: You can also see a sample scenario for pushing data from a native plugin to the UI layer at:

Enjoy!

  • Pingback: Andrew Trice » Blog Archive » Pushing Data to a PhoneGap Web View

  • Pingback: Andrew Trice » Blog Archive » PhoneGap Explained Visually

  • jianhua

    Good article, I like it.
    Currently I am focus on how to improve its performance.

    • http://www.tricedesigns.com Andrew

      You can improve runtime performance of web views by optimizing JS code, and performing any animation/transition with CSS3 transform/translation, which is hardware accelerated.

  • Mitch R

    In the case of simultaneously developing a plugin for iOS/Android can you get away with a single JS file assuming that the native class on the iOS/Android side has the same method calls?

    • http://www.tricedesigns.com Andrew

      As long as the API is identical, yes. That’s the approach it normally try to take.