Developing native extensions for Adobe AIR

 

Before you begin

To make the most of this tutorial, be sure you have the following software installed:

  • Flash Builder 4.6 (sign up for prelease)
  • Adobe AIR 3 (included in Flash Builder) for mobile devices (AIR 2.5 or higher for Adobe AIR for TV-based extensions)
  • Adobe Flex 4.6 (included in Flash Builder)
  • Java API for AS extensions (included in the AIR 3 SDK, under lib/android/FlashRuntimeExtensions.jar)

You also need the following:

  • An Android device for on-device testing.

    You may choose to use another platform, but the steps in the native-code portion of this guide will require that you maintain your own native build environment.

  • A properly installed JDK and the Android SDK.

Additional resources

What are native extensions?

Adobe AIR has allowed application developers to extend the features of the runtime with a set of tools known as Native Extensions for Adobe AIR. This feature was enabled for AIR for TV starting with version 2.5, but it's now been expanded to work on mobile and desktop platforms. By using native extensions, your applications can access all of the features of your target platform, even if the runtime itself doesn't have built-in support.

To illustrate this point, imagine you're creating an application on an Android device, and want to vibrate the phone when your application completes a download. Without native extension support, you'd either have to code the entire program in Java, or use AIR and accept that this task would be impossible. With native extensions, however, you can create a bridge that spans between native code and your own application logic, allowing you to pass instructions back and forth, making it possible for your app to control the vibration motor. You could then leverage the multiplatform support of AIR to deploy this same app to iOS, and expand your native extension by including Objective-C code. You could even change the native code to be platform-aware, allowing you to change the vibration duration and pattern whether the app is running on Android or iOS.

Native extensions allow you to harness unique and platform-specific capabilities of your devices; they also allow you to use native code in ActionScript apps; reuse existing platform code; perform operations in threads to increase your apps' processing power; and give you access to native platform libraries. Native extensions are packaged and distributed like all other ActionScript libraries: you can distribute your own libraries, as well as use native extensions published by other developers, allowing you to plug in functionality to your own applications.

Adobe also provides several fully documented native extension examples that will help developers get started with the above features.

Getting started

This tutorial will get you started creating a native extension of your own. It'll walk you through the steps requried to create native Java code for Android, ActionScript 3 code and a native extension file; you'll also learn how to create a Flex mobile application that works with your native extension, and finally you'll test it on your device. Even though this is a "Hello, World!" tutorial, we'll eschew printing the usual message via native code, and instead opt to control the vibration motor of an Android smartphone. If you're feeling adventerous (or wish to target a different platform) you may choose to adapt the native code section of this guide to to fit a non-Android platform.

Here are the high-level steps that you'll undertake in the following pages:

Creating native code

  1. Set up an Android development environment in Flash Builder 4.6.
  2. Connect and test your Android device.
  3. Create a native Android project.
  4. Create Java code to create an Extension Context.
  5. Create Java code to connect back to ActionScript 3.
  6. Compile your code.

Creating ActionScript 3 code

  1. Set up an ActionScript 3 library project in Flash Builder 4.6.
  2. Code the bridge to your Java code.
  3. Set up a Flex mobile project for your Android phone.

Creating the .ANE file, tying native and ActionScript 3 together

  1. Handle certificates.
  2. Using the adt command.
  3. Modifying your Flex app to use the new .ANE file.

Testing your native extension

Prove that your native extension works!

Creating native code

At the heart of a native extension is the native code, which will act as the first layer between your application and the feature you want to control. In this example, the native code will control the vibration motor and send and receive information with ActionScript 3. You'll write the functions to control the vibration motor yourself, and you'll use the Java APIs provided by Adobe AIR to send data back and forth to ActionScript 3.

Before you can start coding and using libraries, however, you have to set up your build environment.

Setting up an Android development environment using Flash Builder 4.6

Since you'll be writing native Android code, you need a development environment that can operate with the Android SDK. If you haven't already, install and configure the Android SDK by following the links included at the beginning of this guide. Once this is complete, you'll modify Flash Builder to create and compile Android projects:

  1. Open Flash Builder and go to Help > Install New Software (Flash Builder > Window > Install New Software on OS X). (If necessary, close and relaunch Flash Builder with administrative privileges.)
  2. Enter the path to Google's public ADT software repository in the Work with box: https://dl-ssl.google.com/android/eclipse/.Click Add and name the repository Google ADT. Click OK to add the repostiory.
  3. Flash Builder will download a list of available software. Install all developer tools, including Android DDMS, Android Development Tools, Android History Viewer, and Android Traceview. Click Next twice, review the EULAs and decide if you agree, and wait for the packages to install. Some of the Android packages contain unsigned content, but you should continue the installation regardless.
  4. Restart Flash Builder when prompted.
  5. You need to point Flash Builder to the location of your Android SDK. Go to Window > Preferences (Flash Builder > Preferences on OS X) and click Android. Click Browse and select the root location of your Android SDK (for example, C:\Users\dan\Programs\android-sdk). If you're missing any components of the Android SDK, you'll receive an instruction to run the Android SDK manager and install them. Select the most recent version of the SDK and click OK.
  6. Click Window > Android SDK and AVD Manager.
  7. Go to Available Packages and choose the latest Google Inc. packages, to make sure that you have the latest Android SDK and API packages. You may also want to install the USB Driver, depending on your device.
  8. Close this window. You should now be able to click File > New Project and see and entry for Android Project. If so, Flash Builder is properly configured.

Connecting and testing your Android device

Though it's tempting to jump into the specifics of writing code, it'll save hassle later if you now verify that your phone can connect, and is recognized by the Android SDK.

  1. Connect your Android device
  2. On the device, verify that development mode is enabled by going to Settings > Applications > Development, and making sure there's a check in the "USB Debugging" check box.
  3. If necessary, click through the dialogs on your computer that allow you to install the drivers. On Windows, you may want to select the driver located in the android_sdk\usb_driver folder.
  4. Open a command prompt or terminal, and run "adb devices".
  5. You should see your device ID listed. If not, follow guides online for getting adb working with your phone. If that command produces "command not found" or a similar error, add the location of the android_sdk\tools folder to your system PATH variable, and try again.

Creating a new Android project

We now have to create a new Android project in Flash Builder, and instruct the linker to look for the Java API JAR file provided with the AIR SDK.

  1. In Flash Builder, go to File > New > Project.
  2. Select Android Project and click Next.
  3. Name the project HelloANENative.
  4. Note the project location (you can also change the location if you want).
  5. Be sure to select a recent Android SDK for your build target. If you don't see your desired target, return to the previous steps to install the Android SDK inside of Eclipse, or update the SDK to include the APIs and target you want. The rest of the tutorial will be completed with the assumption that you selected Android 2.3.3 or later—but this should not affect any of the following instructions.
  6. Enter the package name, com.<yourdomain>.example.android.helloANE. For example, com.yourDomain.example.android.helloANE.
  7. Click Finish.
  8. Flash Builder will have created a HelloANENativeActivity.java file for you. Open it using the Package Explorer.
  9. Right-click the HelloANENative project in the package explorer, and click Properties.
  10. Click Java Build Path, and then Libraries.
  11. You'll now have to add the Java APIs that allow you to interface with ActionScript 3. Click Add External JARs.
  12. Browse to and select the FlashRuntimeExtensions.jar file (the path to the file is similar to the following: C:\...\Adobe Flash Builder 4.5\sdks\4.5.2\lib\android\
  13. Click OK to dismiss the project propeties dialog box.

You have one more project configuration task: set the application to have permission to use the vibration controls of the device.

  1. Open the AndroidManifest.xml file. You should see the Android Manifest Permissions screen.
  2. Click Add and select Uses Permission, and then click OK.
  3. Under Attributes for Uses Permission select android.permission.VIBRATE.
  4. Save the HelloANENative Manifest file.

You've performed this step so that your native code will have the requisite permission to run should you decide to create native test cases. Although this tutorial doesn't cover it, testing your native code before moving on to ActionScript 3 can be helpful—especially for more advanced native extensions.

What is an extension context?

Now that your Android project is properly configured, you have to start adding in the structures that establish a bridge between ActionScript and your native Java code. The first of these structures is an extension context. An extension context is responsible for containing up to three native extension-related items (see also Oliver Goldman's article, Extending Adobe AIR):

  1. A mapping of native functions to names that ActionScript can reference. This allows ActionScript code to call specific, native functions, and is the heart of a native extension.
  2. A reference to an ActionScript object, which is shared between native code and your AIR app.
  3. A reference to a native code structure, which can only be accessed from native code.

Note that your native extension may have multiple Extension Contexts, and you should separate them based on function. In this example, you only need one, which will provide a map to access the Android vibration feature.

Creating a vibration Extension Context

Next you'll create a new VibrationExtensionContext class.

  1. In the Package Explorer, right-click the src.com.yourDomain.example.android.helloANE package and select New > Class.
  2. Set the package to com.yourDomain.example.android.helloANE.extensions.
  3. Set the name to VibrationExtensionContext.
  4. Set the superclass to com.adobe.fre.FREContext. You can use the Browse button to select this class. This is the AIR Java API that Adobe provides to make the native extension bridge work.
  5. Click Finish to create the new class.

You'll see that two functions have been created for you: public void dispose() and public Map<String, FREFunction> getFunctions() . As you may have guessed, getFunctions() must return a key-value pair mapping between Strings (which are referenced in your ActionScript 3 code), and any FREFunction classes, which you'll define next. The APIs provided by Adobe give you classes and functions that begin with the abbreviation FRE, which stands for Flash Runtime Extension.

Creating a mapping of functions

The first step to defining the functions in your native extension is to create a new Map to return. In the getFunctions() class, add:

@Override public Map<String,FREFunction> getFunctions() { Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>(); return functionMap; }

This creates an empty HashMap, but it clearly isn't very useful if it's empty. You're going to map three key value pairs, each of which will define a class that implements the FREFunction interface:

  • isSupported to a VibrationSupportedFunction. This will run some logic and pass an FREObject to ActionScript 3 containing either true or false , depending on whether the platform supports vibration.

    Note: It's best practice to always allow the ActionScript 3 code to do a compatibility check before using other features of your native extension.

  • vibrateMe to a VibrationVibrateFunction. This FREFunction accepts a parameter from ActionScript 3 controlling the duration of the vibration, and then performs the actual vibration on your device.
  • initMe to a VibrationInitFunction. This function allows the native code to perform and initialization tasks before the other functions are ready to be used. In this example, you'll create a reference to Android's VIBRATOR_SERVICE , which will be used in the VibrationVibrateFunction (AKA "vibrateMe" in ActionScript 3).
  • Inside of the getFunctions() Class, call the put() function on your functionMap object. The first parameter will be a String representing the above function names, and the second parameter a new instance of the (not yet created) function:
functionMap.put("initMe", new VibrationInitFunction()); functionMap.put("isSupported", new VibrationSupportedFunction()); functionMap.put("vibrateMe", new VibrationVibrateFunction());

The first of three functions: VibrationInitFunction

You've defined three functions. Next, you'll write them as classes that implement the FREFunction interface. You'll start with the VibrationInitFunction, which, when called, will set a class in your VibrationExtensionContext that will later be used to vibrate the device.

  1. Right-click a package in the Package Explorer and choose New > Class.
  2. Make the package com.yourDomain.example.android.helloANE.extensions and set the name to VibrationInitFunction.
  3. Leave the Superclass as just a java.lang.Object, and add a new entry to the Interfaces box: com.adobe.fre.FREFunction.
  4. Click Finish.

Note that you can simplify the above steps by using a code-generation feature of Eclipse: click on the class you wish to create (VibrationInitFunction, in this example), hit Ctrl-1 to bring up the code hint window, and select "Create class VibrationInitFunction." This will automatically create the class for you. 

As you'll see, a function inside of your VibrationInitFunction has already been defined for you: call(), which takes two arguments: an FREContext, and an FREObject[] array. By default, these are defined as arg0 and arg1, but you can give them more descriptive names. Change the call() function definition to look like this:

public FREObject call(FREContext context, FREObject[] passedArgs)

When this function is called, the first argument will be a reference to your VibrationExtensionContext, and the second argument will be an array of all arguments (if any) passed down by the ActionScript 3 code. This will be important for your VibrationVibrateFunction, which will set the duration based on the first argument in that array.

For now, your init function is going to use the passed-in FREContext object to get the VibrationExtensionContext, and then the Android Activity it belongs to. Using this activity reference, it will then be able to retrieve the global Android system service known as the Context.VIBRATOR_SERVICE, which will allow you to control the vibrator motor. You'll store this system service in a new variable contained in your VibrationExtensionContext, which you'll create shortly:

  1. In the call() function of VibrationInitFunction , add the following line to grab the VibrationExtensionContext from the FREContext that was passed in:
VibrationExtensionContext vbc = (VibrationExtensionContext)context;
  1. You can now grab the Activity using the VibrationExtensionContext getActivity() function. The inclusion of this function in the FREContext class is designed to support common tasks, such as your need to grab the context's Activity, and thus have a path to the SystemService that we require.
Activity a = vbc.getActivity();
  1. You can now call a.getSystemService(), and pass in a reference to the global Context.VIBRATOR_SERVICE . This will return an object of type Vibrator. You need a place to store this that is available to the entire Extension Context, so put it in a new variable, vb, placed inside the VibrationExtensionContext:
vbc.vb = (Vibrator)a.getSystemService(Context.VIBRATOR_SERVICE);
  1. You should now open the VibrationExtensionContext class, and add a public variable called vb to the class:
public Vibrator vb = null;

Thus, you've created a reference to a native code structure, the vb Vibrator class, which is accessible to any class that can reference your VibrationExtensionContext.

Your completed VibrationInitFunction should look like this:

public class VibrationInitFunction implements FREFunction { @Override public FREObject call(FREContext context, FREObject[] passedArgs) { VibrationExtensionContext vbc = (VibrationExtensionContext)context; Activity a = vbc.getActivity(); vbc.vb = (Vibrator)a.getSystemService(Context.VIBRATOR_SERVICE); return null; } }

You've learned how to: create a class that implements FREFunction; understand the arguments passed in from ActionSscript 3; access your Extension Context via the FREContext argument; and you've seen a common initialization task for extensions.

Next you have to implement the other two FREFunctions that you defined in the Map<String, FREFunction> getFunctions() function.

The second of three functions: VibrationSupportedFunction

The second function that you defined earlier is the VibrationSupportedFunction. As you indicated in the HashMap returned by getFunctions(), this function can be called using the ActionScript 3 String isSupported. The creation of this function is quite similar to the VibrationInitFunction—however, it will show you how to return a Boolean inside of a FREObject.

  1. Right-click a package in the package explorer and click New > Class.
  2. Select com.yourDomain.example.android.helloANE.extensions as the package, and name this class VibrationSupportedFunction, as expected by the value provided in the earlier HashMap.
  3. Choose FREFunction as an Interface that this class will implement.
  4. Click Finish to create the class.
  5. Change the arguments in this class from arg0 and arg1 to context and passedArgs, respectively.
  6. You'll want to return a FREObject as a result in the call() function; create and return that now. You'll also want a reference to your VibrationExtensionContext, so create that by casting the context parameter:
FREObject result = null; VibrationExtensionContext vbc = (VibrationExtensionContext)context; // ... return result;
  1. The logic of this function will be as follows:
  • VibrationInitFunction has already been called, and thus vbc.vc should be set.
  • Is vbc.vc non-null? If so, result should be true.
  • If vbc.vc is null, we can reasonably conclude that initialization failed, and that this platform doesn't support Vibration.result should be set to false.

    Create the following if statement:

if (vbc.vb == null) { result = FREObject.newObject(false); } else { result = FREObject.newObject(true); }
  1. There is one additional task: calling newObject() on FREObject can result in an FREWrongThreadException exception being thrown. You'll surround your if statement in a try catch block, to handle this eventuality.

Your completed call() function should now look like this:

@Override public FREObject call(FREContext context, FREObject[] passedArgs) { FREObject result = null; VibrationExtensionContext vbc = (VibrationExtensionContext)context; try { if (vbc.vb == null) { // Not supported result = FREObject.newObject(false); } else { // Supported result = FREObject.newObject(true); } } catch (FREWrongThreadException fwte) { fwte.printStackTrace(); } return result; }

You now have the second of your three native extension functions: VibrationSupportedFunction. When called by the ActionScript 3 String isSupported , this function will check to see if the variable vb in the VibrationExtensionContext "context" is non-null. It'll return a FREObject that's either true or false based on this condition, and will catch a FREWrongThreadException that's potentially thrown by the static newObject() function of FREObject.

The Third of three functions: VibrationVibrateFunction

The last native extension function you have to implement performs the core duty of your native extension: it allows the AIR application to vibrate the device's motor for a specified duration.

  1. In the Package Explorer, right-click a package and choose New > Class.
  2. Choose com.yourDomain.example.android.helloANE.extensions as the package, and name the class VibrationVibrateFunction.
  3. Make the class implement com.adobe.fre.FREFunction.
  4. Click Finish to create the class.
  5. In the function definition, rename arg0 to context and arg1 to passedArgs.
  6. Create a null FREObject called result.
  7. Cast the context variable to a VibrationExtensionContext variable called vbc. You'll use this to access vbc.vb, the Vibrator object.

    We're now ready to access the first passed argument as a FREObject, and try to set it as an integer. If the data is malformed, an exception may be thrown, which you'll catch. Your call() function should currently look like this:

@Override public FREObject call(FREContext context, FREObject[] passedArgs) { FREObject result = null; VibrationExtensionContext vbc = (VibrationExtensionContext)context; try { // Vibrate the device } catch (Exception e) { e.printStackTrace(); } return result; }
  1. Inside of the try { // ... } block, we're going to try and grab the first element in the passedArgs array as a FREObject:
FREObject fro = passedArgs[0];
  1. We can now create an int by calling getAsInt(); on this FREObject:
int duration = fro.getAsInt();
  1. Finally, call the Android native vibrate function on our vb Vibrator variable, passing in the duration:
vbc.vb.vibrate(duration);

You've now successfully created three native functions, mapped them to strings in the HashMap provided by getFunctions(), and created the native logic necessary to perform all the actions that are required of your native extension. This completes the creation of the VibrationExtensionContext, which is the only extension context that your native extension requires.

Creating the main extension class

You've created the one and only extension context that your native extension requires, but you still haven't created the main class of our extension. Fortunately, adding this class is simple; all we have to do is create a class called VibrationExtension which implements the FREExtension interface.

The FREExtesion interface defines the initialize, dispose, and createContext functions, which allow hooks into the lifecycle of a native extension. Despite providing us with three functions, we only have to customize one: the createContext function. This function has to return a FREContext. Fortunately, you've already created your own VibrationExtensionContext, and you can simply return an instance of this class.

  1. Right-click a package, and select New > Class.
  2. Choose com.yourDomain.example.android.helloANE.extensions as the package name.
  3. Name the class VibrationExtension.
  4. Make the class implement com.adobe.fre.FREExtension by using the Add button next to the interfaces box.
  5. Click Finish to create the class.
  6. By default, the string argument defined in the createContext() function will read arg0 . This argument is actually an ID defining the type of context to create (which is only useful if you have multiple context types). Change arg0 to contextType.
  7. To complete the createContext() function, we only need to return a new instance of our VibrationExtensionContext. Replace the return null; code with the following:
return new VibrationExtensionContext();

This will cause the initialization and creation of your Extension Context, and allow you to use the native code you put in your native extension.

Exporting your native code as a JAR file

In the following sections of this tutorial, we'll see how to code the ActionScript 3 side of our native extension, as well as package and test the completed native extension file and sample application. These steps will involve referencing your native code as a JAR file. Creating a JAR file in Flash Builder is simple:

  1. With your HelloANENative project selected in the Package Explorer, go to File > Export.
  2. Select Java > JAR file and click Next.
  3. Select HelloANENative as the resource to export.
  4. Make sure "Export generated class files and resources" is selected.
  5. Select the HelloANENative folder as the destination for the JAR file, name it HelloANENative and click Finish. You'll use this JAR file both when you create the extension.xml file in your Flex library project, and when you run the packaging command to create your native extension file.

Creating ActionScript 3 Code

You've done the majority of the coding necessary in creating a native extension, having created Java code that you can extend with additional functions, logic, and (if necessary) even additional extension contexts to expand the scope of your native extension.

In contrast, creating the ActionScript 3 code necessary to complete this platform bridge is simple. Your tasks include:

  • Creating and configuring a Flex Library project.
  • Creating an extension.xml file to describe your extension.

Your ActionScript 3 library code will consist of one class, which will import the flash.external.ExtensionContext API, and provide the following functions:

  • A constructor, which will create a new Extension Context of the proper ID, and which will call your initMe native function.
  • A function called isSupported, which will call our isSupported native function, and which will return true or false depending on the response from our native code.
  • A function called vibrate, which will accept a Number duration, and call your native vibrateMe function with this number as a parameter.
  • After completing this code, your ActionScript 3 library function will be complete, and you can move on to packaging and using your native extension. Note that the library function defines the ActionScript 3 code necessary to use your native extension, but it isn't a sample application. In order to use your native extension, you'll have to reference this library application from a Flex mobile application, which you'll create in a later section of this guide.

Creating a Flex library project

Your ActionScript 3 code will exist inside of a Flex Library project:

  • In Flash Builder, open the Flash perspective by selecting it in the top right of the screen.
  • Click File > New > Flex Library Project.
  • Name the project HelloANELibrary.
  • Make sure the Generic library radio button is selected.
  • Make sure that "Include Adobe AIR libraries" is checked. The native extension libraries your project will depend on are included with the AIR APIs.
  • Click Finish to create the project.
  • Open the HelloANELibrary project in your Package Explorer, right-click the src folder, and select New > ActionScript Class.
  • Name the package com.<yourDomain>.nativeExtensions.Vibrate. For example,
  • Name the class Vibrate.
  • Set the superclass to flash.events.EventDispatcher. This will allow this class to dispatch events, which will be useful when you integrate native extensions into real applications.
  • Click Finish to create the Vibrate class.

Coding the ActionScript 3 bridge to your native code

You now have to create the connection to our Extension Context, which will allow you to access the initMe, isSupported, and vibrateMe functions you created in Java.

  1. In the Vibrate class, add a private, static reference to an Extension Context:
private static var extContext:ExtensionContext = null;
  1. In the constructor, you're going to verify whether this extContext variable has been initialized. If not, you'll call the static function ExtensionContext.createExtensionContext(), and pass in two identifiers. The first is an ID you'll set in an extension.xml file shortly. The second is a parameter that's passed to the createContext() function of VibrationExtension. You'll recall that it allows you to create different Extension Contexts; since you only have one, you ignored this parameter in your native code. If you have multiple extension contexts, you should make your native code parse the value you pass in with an if or switch statement, and create the appropriate values based on the available shared String values. Write the following:
if ( !extContext ) { extContext = ExtensionContext.createExtensionContext("com.yourDomain.Vibrate","vibrate"); extContext.call("initMe"); }
  1. Note that you called initMe via extContext.call(), passing in no additional parameters. This will map up with the VibrationInitFunction that you coded in Java, and will initialize the internal data structures necessary for you to vibrate the device.

The Extension Context will now be created and initialized as soon as the Vibrate() constructor is called by any application that uses your new ActionScript 3 library. You still have two functions to implement, however. First create the isSupported() function, which will connect to the native isSupported function, and inspect the Boolean value that is returned by your application logic.

  1. Create a static getter called isSupported, which returns a Boolean:
public static function get isSupported():Boolean { var supported:Boolean = false; // ... return supported; }
  1. Between the two statements, add a call to extContext.call(), passing in isSupported as a String parameter, and which sets your supported variable to the returned Boolean value:
supported = extContext.call("isSupported") as Boolean;

Repeat this process to create the vibrateMe function, which takes in a single Number as the duration. Creation of this function is straightforward:

public function vibrate(duration:Number):void { extContext.call("vibrateMe",duration); }

Note that Flash Builder will automatically compile your library into a SWC file, located in the bin folder of the project. The SWC file is an archive that contains library.swf. You have to manually reference both the SWC and SWF whenever you package an ANE file with ADT. Thus, you should now open the SWC file in an archive management tool, extract library.swf, and place it in the bin/ directory of HelloANELibrary, as well as in the HelloANENative directory:

  1. Navigate to HelloANELibrary/bin/.
  2. Unzip the HelloANELibrary.swc file, or open it in an archive management tool (e.g., 7-Zip).
  3. You'll see a catalog.xml and a library.swf file inside of the SWC archive.

    The library.swf file needs to be placed inside of the native code directory for every platform you target. For example, you'd place this file inside iOS/, android/, x86/, etc., depending on your project's targets. (For more advanced ANEs, you can specify different library.swf files, if you require that your AS3 library is different for different platforms. This goes against the best practice of defining a common interface, however, and it's recommended that you stick with a single version of library.swf.)

  4. When complete, your HelloANELibrary folder should contain both HelloANELibrary.swc, and HelloANENative should contain
    library.swf.

By extracting library.swf, you now have all the files you need to create a native extension. Note that you have to repeat steps 1 through 4 whenever you change your library code, otherwise library.swf will be out of date.

At this point, you've written all of the library code you need to use your native extension.

Describing your native extension in an extension.xml file

You've created the requisite code, but you've yet to link everything together into an ANE file. First, create an extension.xml file inside of your Flex library project. For each native target, this file points to the native code (your JAR file) and describes the package location of the initializer function (and, optionally, a finalizer function, which you don't need in this example). You'll hand this file to the packager when you create your ANE file (which you'll then use in a sample application).

Create the extension.xml file inside of your Flex library project:

  1. Right-click the src folder in your HelloANELibrary project. Click New > File.
  2. Name the file extension.xml.
  3. Re-open this file by right-clicking it and selecting Text Editor, rather than the default XML editor.
  4. The following XML describes the AIR namespace to use (2.5), the ID of the extension, and the single platform we wish to target (note that "iPhone-ARM" is another common target platform):
<extension xmlns="http://ns.adobe.com/air/extension/2.5"> <id>com.yourDomain.Vibrate</id> <versionNumber>1</versionNumber> <platforms> <platform name="Android-ARM"> <!-- ... --> </platform> </platforms> </extension>

Inside of the <platform> tags, you'll now set the location of the JAR file in a <nativeLibrary> tag, and set the location of the initializer to the location we set in the native code (recall that you created the initialize() function in your VibrationExtension class):

<applicationDeployment> <nativeLibrary>HelloANENative.jar</nativeLibrary> <initializer>com.yourDomain.example.android.helloANE.extensions.VibrationExtension </initializer> </applicationDeployment>

You've now successfully created your extension.xml file, and have all the components you need to create your ANE file.

Packaging a native extension

Currently, packaging a native extension requires using the command line tool adt, and passing it a number of parameters. I recommend creating a batch script in Windows (.bat file), or a bash script in OS X (typically a .sh file); the script you'll create will allow you to set your own variables in the top part of the script, allowing it to be easily adapted to your other native extension projects.

There are a number of pieces of information you need to plug into the script. I'll list the information, and show you the values I use on my own system:

  • Location of adt: C:\Program Files\Adobe Flash Builder 4.5\sdks\4.5.2\bin
  • Programming root directory: C:\Users\dan\Programs
  • ActionScript 3 library directory: %root_directory%\HelloANELibrary
  • Android native directory: %root_directory%\HelloANENative
  • Signing options: -storetype pkcs12 -keystore "c:\Users\dan\Programs\ cert.p12"
  • Destination ANE file: HelloANE.ane
  • Location of extension.xml: %library_directory%\src\extension.xml
  • Location of compiled ActionScript 3 library SWC: %library_directory%\bin\HelloANELibrary.swc

You should create a similar list of values for your own system. You can then plug them in by referencing the variables with the following ADT command:

"%adt_directory%"\adt -package %signing_options% -target ane "%dest_ANE%" "%extension_XML%" -swc "%library_SWC%" -platform Android-ARM bin/library.swf -C "%native_directory%" .

This command may look complicated, but it's simply running adt and passing in signing options, specifying ane as the target, providing the extension.xml file, specifying the HelloANELibrary.swc file, targeting Android-ARM as the platform, and telling ADT where to look for the native library files.

A compile_ane.bat file on Windows may look like this:

set adt_directory=C:\Program Files\Adobe Flash Builder 4.5\sdks\4.5.2\bin set root_directory=C:\Users\dan\Programs set library_directory=%root_directory%\HelloANELibrary set native_directory=%root_directory%\HelloANENative set signing_options=-storetype pkcs12 -keystore "c:\Users\dan\Programs\cert.p12" set dest_ANE=HelloANE.ane set extension_XML=%library_directory%\src\extension.xml set library_SWC=%library_directory%\bin\HelloANELibrary.swc "%adt_directory%"/adt -package %signing_options% -target ane "%dest_ANE%" "%extension_XML%" -swc "%library_SWC%" -platform Android-ARM -C "%native_directory%" .

On Mac OS X, the script might look like this:

#!/bin/bash adt_directory="/Users/Applications/Adobe Flash Builder 4.5/sdks/4.5.2/bin" root_directory=/Users/dan/Programs library_directory=${root_directory}/HelloANELibrary native_directory=${root_directory}/HelloANENative signing_options="-storetype pkcs12 -keystore /Users/dan/Programs/cert.p12" dest_ANE=HelloANE.ane extension_XML=${library_directory}/src/extension.xml library_SWC=${library_directory}/bin/HelloANELibrary.swc "${adt_directory}"/adt -package ${signing_options} -target ane "${dest_ANE}" "${extension_XML}" -swc "${library_SWC}" -platform Android-ARM -C "${native_directory}" .

Note that I use a p12 file as my signing certificate. You can substitute the file you normally use for signing AIR files. If you need to create one, the easiest way to do so is to open a Flex or AIR project in Flash Builder, and go to Project > Export Release Build. During the second step you'll have the option to create a certificate using the GUI.

Run your script from the command line, enter your cert's password, and the %dest_ANE% file should be created, and ready to use in a sample application!

Using the native extension in a Flex sample application

You're now going to create a Flex mobile application that uses your native extension! The process of setting up a project and deploying it to Android is simple:

  1. In Flash Builder 4.5.2, click File > New > Flex Mobile Project.
  2. Name the project HelloANESample.
  3. Make sure you're using the 4.5.2 SDK or newer, and click Next.
  4. Make sure that only Google Android is selected as a target platform.
  5. Choose a View-Based application and click Next.
  6. Click Next, as you don't need any server settings.
  7. Click the NativeExtensions tab, which allows you to target any ANE files that your application needs.
  8. Click Add and browse to the ANE file you packaged in the previous step. It should be in the HelloANELibrary folder, named HelloANE.ane.
  9. Click OK. You should see a green check mark next to the ANE file's entry. If you expand this entry, you should see the following warning: "Launching on win32(Windows-x86) is not supported" (or a similar message for OS X). This is because we didn't target our desktop environment when we coded the native portion of our native extension, nor did we configure it when we wrote the extension.xml or ran ADT.
  10. Click Finish.
  11. We'll now double check that the native extension is included in packaging, and not just in the build paths. Although it should be correctly configured, you may have to perform these steps should you modify the native extension. Right-click your project in the Package Explorer and select Properties.
  12. Expand Flex Build Packaging and select Google Android.
  13. Click the Native Extensions tab.
  14. You should see a green check mark next to your native extension, as well as a checkmark in the Package column for that native extension. If not, select the check box.
  15. Click OK to close the project properties.

Configuring permissions

To finish setting up your project, you need to specify that your application requires the use of the Android vibration controls. Pay particular attention to this aspect when you're taking advantage of additional features of a device—it's easy to overlook that some features require additional permissions. The AIR application descriptor won't make these entries available in the commented sections either, as you're going above and beyond the features of the runtime. Should you forget to specify the proper permissions, the native code will fail to work, and will likely throw a permissions-related exception. (On Android, this output is easily visible by using logcat in the adb shell.)

To add permission to the AIR application descriptor:

  1. Right-click the HelloANESample-app.xml application descriptor, and choose to open it with the text editor.
  2. Scroll down to this section:
<android> <manifestAdditions><![CDATA[ <manifest android:installLocation="auto">
  1. Add permission to use the Vibration controls:
<uses-permission android:name="android.permission.VIBRATE"/>

Using your native extension

Now that the project is configured, you can add a vibrate button to the home view:

  1. Between the s:View tags (in the main body of the class), add a new s:Button . Give it a label of Vibrate using ANE, and create a new click handler. Flash Builder should automatically create an fx:Script tag and click handler ActionScript 3 function for you.
  2. Create a new Vibrate class inside of your click handler, which will expose your native extension via an ActionScript 3 object:
var v:Vibrate = new Vibrate();
  1. Trace the value of v.isSupported , and then call your main vibrate function, passing in a hard-coded value of 100 for the number of milliseconds the motor should run:
trace("Is this ANE supported on this platform? " + Vibrate.isSupported); v.vibrate(100);
  1. Click the debug button in the main toolbar.
  2. Select Google Android as the target platform, and choose to launch on the device, with debugging via USB.
  3. Click Debug.

The Flex application should now launch on the device, and provide a button labelled Vibrate using ANE. Tapping this button should produce a 100ms long vibration from the motor in your Android device! You'll also notice the following output in the Console view in Flash Builder:

[SWF] com.yourDomain.Vibrate - 2,916 bytes after decompression [SWF] HelloANESample.swf - 2,702,220 bytes after decompression Is this ANE supported on this platform? True

You can add a TextInput or form of numeric input if you wish to control the duration of the vibration. Simply replace the 100 argument that we hard-coded with a locally scoped variable, and set this variable using a control. At this point, coding the ActionScript 3 side of your application is no different than other Flex application development.

Where to go from here

In this guide you learned that AIR native extensions allow you to expand the capabilties of Adobe AIR, giving your applications access to device and hardware capabilities that would otherwise be inaccessible via the runtime APIs alone. You learned how to create native extensions for Android, and can use these skills to target other platforms. In this example, you focused on the simple task of making the vibration motor of an Android device activate for a specified duration; this illustrated how to create and initialize an native extension, as well as pass data back and forth between your native code and your AIR application.

To achieve this, you:

  1. Coded in native Java to interface with the native extension APIs that Adobe AIR provides (FREObject, FREFunction, etc.).
  2. Coded a Flex library comprised of ActionScript 3 APIs. These hooked into your native APIs.
  3. Wrote an extension.xml describing our extension.
  4. Scripted a batch/bash file to package our native extension with the command line utility ADT.
  5. Created a mobile Flex project that uses our native extension.
  • Although you focused on Android, native extensions work on iOS, Mac OS X, Windows, and Adobe AIR for TV. You can create a single native extension that targets multiple platforms, and your application logic can determine (at runtime or compile-time) if specific features are supported on a platform-by-platform basis.
  • You now have the knowledge and skills necessary to create your own native extensions. Your applications can access additional hardware features, take advantage of native-optimized code or third party libraries, and even spawn multiple threads to handle time-consuming computations without blocking your AIR app.

Including Assets in native extensions

Our example didn't require any assets beyond compiled code—however, you may wish for your native extension to access images, files, database or configuration stores, etc. This is quite possible, and requires a few considerations on mobile:

  • On Android, include your assets "res" folder of your Android project path. These files will be merged into the resources directory of your main application, so you need to choose unique names that won't conflict with your other assets. You can access them using FREContext.getResourceId(), by passing in the desired resource ID (see also Oliver Goldman's article, Extending Adobe AIR).
  • On iOS, resources are accessed via the NSBundle APIs; note that the namespaces are flattened when the project is compiled, and the names you choose for your resources (even if used only in native code) should be guaranteed not to conflict with other resources in your project. For example, don't use two resources anywhere in your project that are both named Localizable.strings (see also Oliver Goldman's article, Extending Adobe AIR).

Dispatching status events

You'll likely find that your native extension must perform asynchronous tasks in native code, and you'll require a way to pass notifications to your AIR application when the task completes. This is accomodated by the function dispatchStatusEventAsync(String code, String level); in the FREContext Class. As an example, the following Java code instructs a hypothetical native extension library that there has been a Status Event with a code of "DATA_CHANGED":

context.dispatchStatusEventAsync("DATA_CHANGED", stringData);
  • This Status Event will be dispatched asynchronously, and (provided your AIR application isn't busy), will immediately be available in the corresponding native extension ActionScript 3 event listener. Since the context is capable of dispatching these events, you'll have to instruct your native extension library to listen for them:
context.addEventListener(StatusEvent.STATUS, onStatus); ... private function onStatus(e:StatusEvent):void { if (e.code == "DATA_CHANGED") { var stringData:String = e.level; // ... } }

Status Events provide a convenient way of updating your native extension library (and thus your resulting AIR and Flex applications) on the status of a native code task.

Learning More

You can continue learning about native extensions by referring to the "additional resources" section at the beginning of this guide. These resources include a link to the native extensions that Adobe has already created and distributed, allowing you to expand AIR's capabilities simply by dropping native extension files into your Flex and ActionScript 3 applications.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章