Creating AIR Native Extensions for BB10

by Administrator on ‎10-26-2011 01:36 PM - edited on ‎03-22-2012 04:11 PM by BlackBerry Development Advisor (15,304 Views)

Author: Paulo Garcia

 

 

Before Starting

 

You will have to have installed and configured both the BlackBerry® Native SDK  2.0 for BlackBerry® Tablet OS and the BlackBerry Tablet OS SDK 2.0 for Adobe® Air®. We’re also going to use Adobe® Flash Builder® to create the Adobe® ActionScript® side and the sample application. However, you are able to accomplish the same tasks using the command line only.

 

If you’re planning to test the application shown here in an actual device, make sure you have requested code signing keys and created debug tokens. The simulator doesn’t require debug token for testing.  For more information on code signing, refer to this link:  https://bdsc.webapps.blackberry.com/CodeSigningHelp/

 

Tools you are going to need

 

  • BlackBerry® Native SDK 2.0 for BlackBerry® Tablet OS
  • BlackBerry® Playbook™ tablet running BlackBerry Tablet OS 2.0
  • BlackBerry Tablet Simulator running BlackBerry Tablet OS 2.0 (optional)
  • Adobe® Flash Builder® 4.6 (optional – command line tools can also be used)
  • Adobe® Air® SDK 2.7
  • BlackBerry Tablet OS SDK for Adobe Air 2.0 or later.

What is Air Native Extension (ANE)?

 

An ANE is a combination of ActionScript and Native code, allowing developers to extend Air runtime capabilities in areas where the core runtime doesn’t cover, normally related to device-specific capabilities.

 

As probably you already know, Air applications run in a protected environment (sandbox), and because of that such application doesn’t have full access to the system resources. The native extension interface allows native code to run still keeping the Air security model.

 

The document Developing ACTIONSCRIPT® Extensions for ADOBE® AIR® available on the Adobe website provides further explanation on ANEs.

 

An ANE can be used is different projects, or be specific for one particular application. In this tutorial you will learn how to create the ANE file in a way you can use it on any application.

 

Creating the ActionScript library

 

Our first step is to create an ActionScript library that interfaces with the native code. 

 

This library will be created using Flash Builder 4.6, but the same can be accomplished using command-line tools.

 

To create the project on Flash Builder 4.6:

 

  1. Create a new Flex Library Project, and call it nativeAS3.
  2. Make sure you have Mobile Library selected in the same dialog window, and leave all other options with default values.
  3. Click Finish.

 

This project artifact  is a .swc file – the ActionScript library file. But for now, as you can see, there is no source code under src folder, and this is what you are going to add now:

 

  1. Right-click the src folder and select New -> ActionScript Class.
  2. When the dialog opens, use the names showed below:

ANE_Screenshot3.png

 

package com.example
{
	
	import flash.external.ExtensionContext;
	
	public class NativeAS3
	{
		private var _ctx:ExtensionContext;
		
		public function NativeAS3()
		{
			this._ctx = ExtensionContext.createExtensionContext( 
 						"com.example.NativeCode", null);
			
		}
		public function sayHello( ): Object {
			var result:Object = this._ctx.call( "sayHello" );
			return result;
		}
	}
}

 

As you can see, the code above implements the ActionScript side of the method sayHello(), which will actually be calling the shared object functions with the same name.

 

Note: The string "com.example.NativeCode" matches the name used on the native extension project created using the NDK.

 

Next, we will be creating the native code itself, and using the new NDK tooling to create the ANE file.

 

Creating the Native side

 

Setup

 

A native extension is no more thsn a C/C++ shared object, which contains certain functions that act as entry-points to setup the interface used by the ActionScript code. Again, for details about the ANE structure, check the document mentioned above.

 

For this tutorial, I am using the Blackberry Native SDK 2.0, available at Blackberry Developer Zone, and I am assuming you have basic familiarity with the IDE, and you read the Getting Started Guide. The version 2.0 brought new tools that help the native extension creation, from project templates to creating the final product.

 

That said, let’s get started creating our shared object:

 

  1. Open the NDK for BlackBerry Tablet OS.
  2. Select File -> New -> BlackBerry Tablet OS C/C++ Project.
  3. Give this project the name NativeCode, and click Next.
  4. On the next step you will be presented to several project types. Select under the new AIR Native Extension the Hello AIR Native Extension option. 
  5. Click Finish.

At this point, your project will be created. Note that if this is the first time you are using the NDK, the Deployment Setup Wizard will be displayed, allowing you to configure your environment. Check the BlackBerry Native SDK Getting Started Guide and User Guide for more information about it.

 

This project template contais the barebones of a native extension, and a function called sayHello, which will be used to demonstrate the whole process.

 

In this tutorial, I’m going to create the shared object to be used on the device and also on the simulator. Remember that the simulator uses x86 binaries whereas the Playbook uses arm7. 

 

The Code

 

The code created is listed here for reference:

 

#include <FlashRuntimeExtensions.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void extensionInitializer(void** extDataToSet,
		FREContextInitializer* ctxInitializerToSet,
		FREContextFinalizer* ctxFinalizerToSet);
void extensionFinalizer();
void contextInitializer(void* extData, const uint8_t* ctxType,
		FREContext ctx, uint32_t* numFunctionsToSet,
		const FRENamedFunction** functionsToSet);
void contextFinalizer(FREContext ctx);

// Exposed functions

FREObject sayHello(FREContext ctx, void* functionData, uint32_t argc,
		FREObject argv[]);

FREObject sayHello(FREContext ctx, void* functionData, uint32_t argc,
		FREObject argv[]) {
	const char *out = "[nativecode.c] - Hello from the native code\n";
	fprintf(stdout, out);
	fflush(stdout);
	FREObject result;
	FRENewObjectFromUTF8((uint32_t)(strlen(out) + 1), (uint8_t*) out, &result);
	return result;
}

/**
 * Defines the extension initializer that is called each time
 * a NativeContext instances isinitialized.
 */

void contextInitializer(void* extData, const uint8_t* ctxType,
		FREContext ctx, uint32_t* numFunctionsToSet,
		const FRENamedFunction** functionsToSet) {
	fprintf(stdout, "[nativecode.c] - SampleContextInitializer Begins\n");
	fflush(stdout);

	const char *functionNames[] = { "sayHello", NULL };
	FREFunction functionPtrs[] = { sayHello, NULL };

	// count number of functions
	*numFunctionsToSet = 0;
	while (functionPtrs[*numFunctionsToSet] != NULL) {
		(*numFunctionsToSet)++;
	}

	FRENamedFunction *functionSet= calloc (*numFunctionsToSet, sizeof(FRENamedFunction));
	char *temp = NULL;
	int i;
	for (i = 0; i < *numFunctionsToSet; i++) {
		int bufferSize = sizeof(char) * (strlen(functionNames[i]) + 1);
		temp = (char*) malloc(bufferSize);
		strncpy(temp, functionNames[i], bufferSize);
		temp[strlen(functionNames[i])] = '\0';
		functionSet[i].name = (uint8_t*) temp;
		functionSet[i].functionData = NULL;
		functionSet[i].function = functionPtrs[i];
	}
	*functionsToSet = functionSet;
	fprintf(stdout, "[nativecode.c] - SampleContextInitializer Ends\n");
	fflush(stdout);
}

/**
 * Defines the extension finalizer that is called each time
 * a NativeContext instances is disposed.
 */

void contextFinalizer(FREContext ctx) {
	fprintf(stdout, "[nativecode.c] - SampleContextFinalizer\n");
	fflush(stdout);
}

void extensionInitializer(void** extDataToSet,
		FREContextInitializer* ctxInitializerToSet,
		FREContextFinalizer* ctxFinalizerToSet) {
	fprintf(stdout, "[nativecode.c] - SampleExtentionInitializer\n");
	fflush(stdout);
	*extDataToSet = NULL;
	*ctxInitializerToSet = &contextInitializer;
	*ctxFinalizerToSet = &contextFinalizer;
}

void extensionFinalizer() {
	fprintf(stdout, "[nativecode.c] - Extension Finished\n");
	fflush(stdout);
	return;
}

 

It is not the scope of this tutorial explaining details of the code above, but there few highlights that worth mentioning:

 

  • FlashRuntimeExtensions.h is provided by Adobe, but it is already available in the NDK.
  • The function sayHello() is the one you will be able to call directly from your ActionScript code.
  • The initializer/finalizer functions are mandatory and part of the ANE interface between the ActionScript and the native code.

 

Building the Project

 

In this tutorial, I’m going to create the shared object to be used on the device and also on the simulator. Remember that the simulator uses x86 binaries whereas the Playbook uses arm7. 

 

To build the only Device-Debug and Simulator-Debug targets at once, the best way is:

 

  1. Right-click the project name on your Project Explorer view
  2. Select Build Configurations… -> Build Selected…. The following dialog will be displayed:

ANE_Screenshot1.png

 

 

3. Select Device-Debug and Simulator-Debug, and click OK.

4. After the build is done, you will see both binaries created: libNativeCode-arm.so and libNativeCode-x86.so



 

 ane-project-after-build.png

 

 

Creating the ANE file

 

The NDK 2.0 does an excellent job helping you to create the native extension file (ANE) with few steps. For that, you just have to:

 

  1. Open the NDK IDE, and select the NativeCode project.
  2. Right-click and select Export.
  3. On the Export dialog, select BlackBerry Tablet OS -> AIR Native Extension. The following dialog box will be displayed:

ane-export-1.png

 

 4. Change the Base filename to com.example. This is not mandatory, but only to keep the steps clearer in this tutorial

 5. The AIR SDK field has to point to the Flash Builder sdks folder (as illustrated above), or the place where the standalone Air SDK was installed.

 6. Library SWC should be filled in with the NativeAS3.swc file you created before using Flash Builder.

 7. Click Finish to start the build process.

 

Note: If during the build process, you see an error message like "Invalid swc file. The extension NameSpace requires the SWF verison to be 13 or lower", you will have to go back to the Flash Builder project and add the compiler parameter "-swf-version 13" (Project Properties -> Flex Library Compiler) to force the right swf version for the library.

 

If everything is fine, you will see a com.example.ane file created in the NDK Project Explorer. This file will be used in the next section, to create the sample application that uses the native extension.

 

Creating the Sample Air Application

 

The sample Air application is a simple application that will use the ANE. For this tutorial it will consist in a simple screen with the button Hello. To create the project, and assigning the ANE file you should:

 

 

1. Create a new ActionScript Mobile project, and call it UsingANE.

2. Unselect iOS and Android support (optional)

3. The last step on the New Project wizard has three tabs: Source Path, Library Path and Native Extensions. Select the latter.

4. Click Add ANE button, and choose the com.example.ane file created before:

 

ane-fb-1.png

 

5. Click Finish.

6. An empty project will be created. 

7. Open the project Properties, navigate to ActionScript Build Packaging -> BlackBerry Tablet OS, and selectAdd platform specific libraries to library path

8. On the same dialog, click the Native Extensions tab.  You'll see the com.example.ane file is listed there, but you also have to select to add it to the Package. If you don't see the checkbox after the com.example.ane item, you have to enlarge the dialog in order to see it.

 

fb-ane-2.png

 

9. To add the ANE to the BAR file, check to option Package. At this point, Flash Builder will ask for confirmation. Just confirm and click OK to close the project Properties.

10. Copy the following code into your main source file (UsingANE.as):

 

/*
* A simple example that demonstrates use of AIR native extension
* by invoking the native code, getting the printf output from the
* native code, and display on the screen.
*/

package
{
	import com.example.NativeAS3;
	
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	
	import qnx.dialog.PromptDialog;
	import qnx.ui.buttons.LabelButton;
	import qnx.ui.text.Label;
	
	[SWF(height="600", width="1024", frameRate="30", backgroundColor="#BBBBBB")]
	public class UsingANE extends Sprite
	{
		private var helloLabel:Label;
		private var helloButton:LabelButton;
		private var extension:Object;
		
		public function UsingANE()
		{
			/* A button to run native code. */
			helloButton = new LabelButton();
			helloButton.label = "Hello";
			helloButton.width = 150;
			helloButton.x = stage.stageWidth / 2 - helloButton.width / 2;
			helloButton.y = stage.stageHeight / 2;

			/* A label in which to show the hello greeting. */
			helloLabel = new Label();
			helloLabel.width = 800;
			helloLabel.height = 30;
			helloLabel.x = (stage.stageWidth - helloLabel.width) / 2;
			helloLabel.y = helloButton.y - 60;
			
			var format:TextFormat = new TextFormat();
			format = new TextFormat();
			format.align = TextFormatAlign.CENTER;
			format.font = "Arial";
			format.color = 0x103f10;
			format.size = 24;
			helloLabel.format = format;
			addChild(helloLabel);
			
			/* Listen for a click on the button. */
			helloButton.addEventListener(MouseEvent.CLICK, onHelloButtonClick);
			addChild(helloButton);
			stage.nativeWindow.visible = true;
		}
		
		private function onHelloButtonClick(event:MouseEvent):void
		{
			this.extension = new NativeAS3();
			var out:String = new String(this.extension.sayHello());
			helloLabel.text = out;
		}
	}
}

 


Your sample application should also be informed that the native extension called com.example.NativeAS3 is present and available to be used. Since you've added the native extension using Flash Builder tools, this is already done, but if you are using command line instead, you must add the highlighted tag <extensions> in your *-app.xml file (in our case UningANE-app.xml):

 

<application xmlns="http://ns.adobe.com/air/application/2.6">
(…)
    <extensions>
       <extensionID>com.example.NativeAS3</extensionID>
    </extensions>
(…)
</application>

  

Deploying to the Playbook

 

To be able to run native code from an Air application, you must add to the bar file descriptor (blackberry-tablet.xml or bar-descriptor.xml) the appropriated <action> tag:

 

<action system="true">run_air_native</action>

 

As you probably know, when deploying to an actual device, not the simulator, you have to use a debug token or sign the .bar file.

 

Only the Air application that will use and call native methods, has to be sign, so in our tutorial, only the UsingANE must be signed. It is not necessary to sign the shared object created with NDK 1.0 or the ActionScript library (.swc file).