Building a Native Extension | Part 2 – The ActionScript Code

In this post we’ll discuss the parts of the native extension that are written in ActionScript.  This includes in the main library and the default implementation.

The ActionScript Library

The main library is an ActionScript library which serves as an interface to the AIR application.  This library is also responsible for interacting with the extension context generated by AIR.  The extension context is used as a bridge between AIR and the native platform.

1.  Create a Flex Library project.

2.  Create a Controller class that extends EventDispatcher.

[sourcecode language=”actionscript3″]
public class VolumeController extends EventDispatcher {
public function VolumeController( enforcer:SingletonEnforcer ) {
super();
}
}
[/sourcecode]

 

 

3.  Initialize the extension context.

[sourcecode language=”actionscript3″]
extContext = ExtensionContext.createExtensionContext("net.digitalprimates.volume", "" );
if ( !extContext ) {
throw new Error( "Volume native extension is not supported on this platform." );
}
[/sourcecode]

 

 

The extension context is the bridge between the AIR application and the native platform.  The createExtensionContext method accepts two parameters.  The first is the package of the native extension; this is defined in the extension.xml which we’ll cover in a minute.  The second is an optional parameter that defines the particular behavior desired from the native extension.  For simple native extensions this won’t apply, but in more complex native extensions this serves as a valuable way to separate the native code.  For example, if you had a native extension to send notifications you may have a local implementation and a push implementation.

The volume native extension is pretty basic so we pass the package path and leave the second parameter empty.

If the result from createExtensionContext is null there isn’t an implementation in the ANE for the native platform that the AIR application is currently running on.  In that case you should throw an error.

4. Expose the interface methods and call native methods on the extension context.

The volume extension has two native methods, init() and setVolume().

[sourcecode language=”actionscript3″]
private function init():void {
extContext.call( "init" );
}
public function setVolume( newVolume:Number ):void {
if ( isNaN(newVolume) )  {
newVolume = 1;
}
if ( newVolume < 0 ) {
newVolume = 0;
}
if ( newVolume > 1 ) {
newVolume = 1;
}

extContext.call( "setVolume", newVolume );
systemVolume = newVolume;
}
[/sourcecode]

 

 

The VolumeController is a singleton, since it doesn’t make sense to need more than one thing controlling the system volume.  The init() method is called when the singleton instance is created.

The setVolume() method expects a value between zero and one, so some basic validation is performed and then the volume is passed to the native extension.

5.  Create the extension.xml file.

The extension.xml defines where the code for each platform lives.  This is how the native extension knows which platforms are supported and how to access the native libraries.

[sourcecode language=”xml”]
<extension xmlns="http://ns.adobe.com/air/extension/3.1">
<id>net.digitalprimates.volume</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>libAndroidVolumeLib.jar</nativeLibrary>
<initializer>net.digitalprimates.volume.VolumeExtension</initializer>
</applicationDeployment>
</platform>
<platform name="iPhone-ARM">
<applicationDeployment>
<nativeLibrary>libIOSVolumeLib.a</nativeLibrary>
<initializer>VolumeExtensionInitializer</initializer>
<finalizer>VolumeExtensionFinalizer</finalizer>
</applicationDeployment>
</platform>
<platform name="default">
<applicationDeployment/>
</platform>
</platforms>
</extension>
[/sourcecode]

For now just leave the Android and iPhone platform nodes empty.  Once the native libraries are generated you can fill them in.

The id is the same id that is requested when the extension context is built.

In the platforms node you should put one platform node for each platform you plan on supporting.  Here I’m supporting Android and iOS.  Other possible platforms are OSX, Windows, and AIR TV.

The nativeLibrary node points to the location of the native library.

The initializer points to the initializer class for the native library.

The finalizer points to the finalizer class for the native library.

We’ll dicuss initializers and finalizers more in a few minutes.

The default platform defines an ActionScript implementation to use on any platform that isn’t defined in the extension.xml.  Generally this will be some sort of stub implementation as it’s often difficult to reproduce native behavior in ActionScript (sort of the whole reason for writing a native extension in the first place).  I think it’s important to add a default implementation so that your application can be tested in the Flash Builder simulator.  We’ll cover how to create the default implementation later.

At this point to main library is done.  We have defined the interface to interact with the native extension, we have connected to the extension context bridge, and we’ve created the extension.xml file to define the available platforms and their options.

The Default Library

The default library is the code that will be used on any platform that isn’t specifically supported.  The main reason I’m including this is so that the native extension will work in the Flash Builder simulator; it would be pretty painful if you had to deploy to the device every time you wanted to see a small change.  However, it’s also important if you’re creating an extension that’s targeted specifically at one platform but the main application supports multiple platforms.

Creating the library is actually pretty easy, as the default library is an exact copy of the main ActionScript library but with all of the methods stubbed out.  It’s important that every class that exists in the main ActionScript library also exists in the default library.

In the Volume extension I let the default library continue to update the systemVolume property whenever setVolume() is called.  Also, the systemVolume property defaults to a value of 1.  This lets the native extension ‘work’ in the simulator, emulating what would happen if the user never touched the hardware volume buttons.  The native extension is not able to actually change the volume of the simulator though, so only UI pieces will appear to work.

See the VolumeDefault project for more information.

Instructions:

1) Make a new library project.

2) Create a copy of each class from the main ActionScript library project.

3) Stub out the methods for each class.  Be sure that there are no references to an extension context.

Continue to part 3

2 Comments

    Hello Nathan

    I am trying follow your steps but Flash Builder 4.7 always creates a .swc file instead of a .swf file.

    What to do? As in your git repo and in the final chapter it uses .swf file.

    Thanks for your help.
    Cheers!

    • by Aman
    • 5:55 am, January 20, 2015
    • Reply

      You need to rename the *.swc file to *.zip and then extract the contents, which will be a *.swf.

      • by Nathan Weber
      • 6:21 am, January 20, 2015
      • Reply

Leave a Reply to Aman Cancel reply