原文:http://www.adobe.com/devnet/air/articles/developing-native-extensions-air.html
以下为摘译
Note: Adobe recommes using the next version of Flash Builder for developing native extensions for Adobe AIR.Sign up now to get access to the prerelease of Flash Builder 4.6.
Adobe强烈建议使用下一代Flash Builer开发native extension。
This tutorial introduces you to creating your own native extensions for Adobe AIR using simple code. In addition to showing you code and skills that are common across all native extensions (AIR, ActionScript 3, and Flash Builder), this tutorial covers the compilation of native code on the Android platform. Although you may be interested in targeting Objective-C, C#, C++, C, or some other language, the Java code should give you a solid understanding of native interaction with native extensions.
本例使用Java
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)
- 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
- Android SDK
- Installing the Android SDK
- Developing ActionScript Extensions for Adobe AIR
- Adding Android Development Tools (ADT) to Flash Builder 4.5.1
1、FlashBuilder4.6
2、Adobe AIR3 for mobile
3、一个Android设备用来测试
4、为Flash Builder4.5.1安装ADT
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
- Set up an Android development environment in Flash Builder 4.6.
- Connect and test your Android device .
- Create a native Android project.
- Create Java code to create an Extension Context.
- Create Java code to connect back to ActionScript 3.
- Compile your code.
1. 在FB4.6中建立Android开发环境
2. 连接Android设备
3. 创建native Android工程
4. 用Java创建Extension Context
5. 用Java关联AS3
6. 编译
Creating ActionScript 3 code
- Set up an ActionScript 3 library project in Flash Builder 4.6.
- Code the bridge to your Java code.
- Set up a Flex mobile project for your Android phone.
1.在FB4.6中创建AS3的library工程
2.桥接Java代码
3.创建Flex Mobile工程
Creating the .ANE file, tying native and ActionScript 3 together
- Handle certificates.
- Using the adt command.
- Modifying your Flex app to use the new .ANE file.
创建ANE,把native和AS3整合在一起
1.证书问题
2.使用adt命令
3.在Flex程序中使用ANE
Testing your native extension
- Prove that your native extension works!
Creating native code
在本例中,native code将控制vibration motor,由AS3发出和接收消息。由你来写控制vibration motor的代码,用AIR提供的Java API把数据发给AS3。
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:
既然你要编写native Android code,你需要能够操作Android SDK的开发环境
- 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.)
打开FB,Help>Install New Software - 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.
在Work里输入Google的ADT路径:https://dl-ssl.google.com/android/eclipse/。点击OK。 - 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.
FB将下载一个可用软件的列表,包括Android DDMS,Android Development Tools,Android History Viewer和Android Traceview。点击Next,处理许可,等待package下载。有些Android的packages含有usigned的content,但是你应该无视它们,继续安装。 - Restart Flash Builder when prompted.
重启动FB。 - 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.
在FB中指定Android SDK的位置。Windows > Preferences,点击Android。设定Android SDK的路径 - Click Window > Android SDK and AVD Manager.
打开SDK and AVD Manager - 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.
下载最新的SDK和API,视需要下载USB Driver。 - 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.
关闭这个对话框,如果能在File > New 后,从Wizard看到Android工程,则说明配置成功
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.
看一下SDK能否识别你的Phone
- Connect your Android device
连接Android设备 - 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.
- 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.
- Open a command prompt or terminal, and run "adb devices".
- 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.
- In Flash Builder, go to File > New > Project.
- Select Android Project and click Next.
- Name the projectHelloANENative.
- Note the project location (you can also change the location if you want).
- 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.
本例要求Android在2.3.3之上 - Enter the package name,com.<yourdomain>.example.android.helloANE. For example, com.yourDomain.example.android.helloANE.
- Click Finish.
- Flash Builder will have created a HelloANENativeActivity.java file for you. Open it using the Package Explorer.
FB会创建一个java文件。 - Right-click the HelloANENative project in the package explorer, and click Properties.
- Click Java Build Path, and then Libraries.
- You'll now have to add the Java APIs that allow you to interface with ActionScript 3. Click Add External JARs.
需要添加Java API,允许你与AS3交互(能否理解为:为Android开发增加一个操作AS3的库) - 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\
- 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.
要让程序有使用设备的vibration controls的权限
- Open the AndroidManifest.xml file. You should see the Android Manifest Permissions screen.
打开AndroidManifest.xml。 - Click Add and select UAndroidManifest.xmlses Permission, and then click OK.
- Under Attributes for Uses Permission select android.permission.VIBRATE.
- 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):
建立桥接AS和native Java代码。第一个结构体是extension context。extension context负责包含三个native extension相关的内容。
- 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.
native函数的map。AS将通过它调用特定的native function - A reference to an ActionScript object, which is shared between native code and your AIR app.
AS对象的引用,将在native code和AIR程序中共享。 - A reference to a native code structure, which can only be accessed from native code.
native的代码结构,只在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.
你的native extension可以含有多个Extension Context,你应该根据函数分离它们。
Creating a vibration Extension Context
Next you'll create a new VibrationExtensionContext class.
创建新的VibrationExtensionContext类
- In the Package Explorer, right-click the src.com.yourDomain.example.android.helloANE package and select New > Class.
选择刚才创建的包,创建一个新的类,New > Class - Set the package to com.yourDomain.example.android.helloANE.extensions.
设置所属于哪个package - Set the name to VibrationExtensionContext.
设置类名 - 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.
设置superclass - Click Finish to create the new class.
You'll see that two functions have been created for you:public void dispose()
andpublic 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.
你会发现有两个函数dispose和getFunctions已经有了。
Creating a mapping of functions
The first step to defining the functions in your native extension is to create a newMap
to return. In thegetFunctions()
class, add:
第一步是创建一个新的Map,返回它
@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 theFREFunction
interface:
此处创建了一个空的HashMap,稍后需要三个key value对,每个都定义了一个实现自FREFunction的类
-
isSupported
to a VibrationSupportedFunction. This will run some logic and pass an FREObject to ActionScript 3 containing eithertrue
orfalse
, depending on whether the platform supports vibration.
isSupported对应VibrationSupported函数。
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.
最好的办法是始终允许AS3代码在使用native extensiong前进行兼容性检查 -
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.
vibrateMe对应VibrationVibrate函数。这个函数接收一个来自AS3的参数,控制vibration的duration,接下来在你的设备上执行实际的vibration。 -
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'sVIBRATOR_SERVICE
, which will be used in the VibrationVibrateFunction (AKA "vibrateMe" in ActionScript 3).
initMe对应VibrationInitFunction。这个函数是在其他函数被使用前进行初始化。在本例中,你将要创建一个针对Android VIBRATOR_SERVICE的引用。 - Inside of the
getFunctions()
Class, call theput()
function on yourfunctionMap
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:
在类getFunctions中,在functionMap上调用put函数。第一个参数是函数名称,第二个参数是函数的实例
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.
从VibrationInitFunction开始
- Right-click a package in the Package Explorer and choose New > Class.
添加一个新类 - Make the package com.yourDomain.example.android.helloANE.extensions and set the name to VibrationInitFunction.
确认所在的包和类名 - Leave the Superclass as just a java.lang.Object, and add a new entry to the Interfaces box: com.adobe.fre.FREFunction.
确定Superclass,添加新的接口 - Click Finish.
As you'll see, a function inside of your VibrationInitFunction has already been defined for you:call()
, which takes two arguments: anFREContext
, and anFREObject[]
array. By default, these are defined asarg0
andarg1
, but you can give them more descriptive names. Change thecall()
function definition to look like this:
此处会产生一个新的函数call,有两个参数,可以把参数名称换成以下,更为易读
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.
当调用这个函数时,第一个参数是你的VibrationExtensionContext的引用,第二个参数是一个参数的列表。
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:
你的init函数将使用参数context获得VibrationExtensionContext,接下来获得属于它的Android Activity。使用activity引用,可以获得全局Andorid system service,称为VIBRATOR_SERVICE,它允许你控制vibrator motor,你可以把它保存在Context的成员变量里。
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;
在call中,通过以上方式先获得Context
2.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();
接下来获取Activity。
3.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);
获取SERVICE并保存起来
public Vibrator vb = null;
定义一个变量来保存SERVICE
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 theFREContext
argument; and you've seen a common initialization task for extensions.
到此为止,你学会了:
1、创建实现自FREFunction的类
2、理解从AS3传入的参数
3、通过FREContext参数访问Extension Context
4、一般性的初始化
Next you have to implement the other two FREFunctions that you defined in theMap<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 bygetFunctions()
, this function can be called using the ActionScript 3 StringisSupported
. 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.
- Right-click a package in the package explorer and click New > Class.
- Select com.yourDomain.example.android.helloANE.extensions as the package, and name this classVibrationSupportedFunction, as expected by the value provided in the earlier HashMap.
- Choose FREFunction as an Interface that this class will implement.
- Click Finish to create the class.
以上四步创建一个新类,方法同前一个类 - Change the arguments in this class from
arg0
andarg1
tocontext
andpassedArgs
, respectively.
参数改名,更易读
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;
设置返回值,获得Context
7. 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.
if (vbc.vb == null)
{
result = FREObject.newObject(false);
}
else
{
result = FREObject.newObject(true);
}
根据vbc.vb是否为null判定结果,如果不为空,则返回true,反之为false
8.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.
这里要额外做一件事:调用newObject可能会产生异常,你需要增加异常处理。
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 StringisSupported
, this function will check to see if the variablevb
in the VibrationExtensionContext "context" is non-null. It'll return a FREObject that's eithertrue
orfalse
based on this condition, and will catch a FREWrongThreadException that's potentially thrown by the staticnewObject()
function of FREObject.
当AS3调用isSupport时,这个函数会检查Context中的vb变量返回true或者false
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.
第三个函数允许AIR程序针对特定的duration vibrate设备的motor。
- In the Package Explorer, right-click a package and choose New > Class.
- Choose com.yourDomain.example.android.helloANE.extensions as the package, and name the classVibrationVibrateFunction.
- Make the class implement com.adobe.fre.FREFunction.
- Click Finish to create the class.
- In the function definition, rename
arg0
to context andarg1
topassedArgs
.
以上几步创建类,修改参数变量名 - Create a null FREObject called
result
.
创建返回值
vbc
. You'll use this to accessvbc.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. Yourcall()
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;
}
8. 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];
参数中获得第一个元素
9.We can now create an int by calling getAsInt(); on this FREObject:
int duration = fro.getAsInt();
根据刚刚获得的fro获得int
10.Finally, call the Android native vibrate function on our vb Vibrator variable, passing in the duration:
vbc.vb.vibrate(duration);
最终,调Android的native vibrate函数
You've now successfully created three native functions, mapped them to strings in the HashMap provided bygetFunctions()
, and created the native logic necessary to perform all the actions that are required of your native extension. This completes the creation of theVibrationExtensionContext
, 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.
你已经创建了唯一的一个extension context,但是还没有为extension创建主class。
The FREExtesion interface defines theinitialize
,dispose
, andcreateContext
functions, which allow hooks into the lifecycle of a native extension. Despite providing us with three functions, we only have to customize one: thecreateContext
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.
接口FREExtension定义了initialize,dispose和createConetxt,这些函数用于控制native extension的生命周期。
- Right-click a package, and select New > Class.
- Choose com.yourDomain.example.android.helloANE.extensions as the package name.
- Name the class VibrationExtension.
- Make the class implement com.adobe.fre.FREExtension by using the Add button next to the interfaces box.
- Click Finish to create the class.
创建一个新类,实现FREExtension接口
createContext()
function will readarg0
. This argument is actually an ID defining the type of context to create (which is only useful if you have multiple context types). Changearg0
tocontextType.修改参数arg0为contextType,这样更易读
7. To complete the
createContext()
function, we only need to return a new instance of our VibrationExtensionContext. Replace thereturn 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.这会导致你的ExtensionContext的初始化和创建,允许你使用native code。
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:
在FB中创建一个JAR文件的步骤如下
- With your HelloANENative project selected in the Package Explorer, go to File > Export.
- Select Java > JAR file and click Next.
- Select HelloANENative as the resource to export.
- Make sure "Export generated class files and resources" is selected.
- Select a destination for the JAR file, name it HelloANENative.JAR 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.
创建配置Flex库工程 - Creating an extension.xml file to describe your extension.
创建描述extension的extension.xml
你的AS3库包含一个类,它需要importflash.external.ExtensionContext,提供以下功能。
- A constructor, which will create a new Extension Context of the proper ID, and which will call your initMe native function.
构造函数,它将会创建一个新的Extension Context。它会调用你的InitMe native函数。 - A function called isSupported, which will call our isSupported native function, and which will return trueor false depending on the response from our native code.
函数isSupported,调用isSupported native函数,该函数会根据native code的处理返回true或flase。 - A function called vibrate, which will accept a Number duration, and call your native vibrateMe function with this number as a parameter.
函数vibrate,接收一个数值duration。它会以这个值为参数调用native的vibrateMe函数。 - 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.
做完这些之后,AS3库的工作告一段落,你可以使用native extension来打包。需要注意的是,虽然这些库函数可以用你的native extension,但它不是一个范例程序。为了使用native extension,你需要创建一个Flex mobile 程序。
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.
创建一个新的Flex Library工程。 - 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.
在该库中创建一个类Vibrate。
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.
现在需要创建一个面向Extension Context的连接。通过它访问Java中创建的函数
1.In the Vibrate class, add a private, static reference to an Extension Context:
在class Vibrate中,增加一个静态变量,准备指向Extension Context
private static var extContext:ExtensionContext = null;
2.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:
在构造函数中,你要检查一下extContext是否已经被初始化,如果没有,你要调用static functionExtensionContext.createExtensionContext()。该函数需要两个参数,第一个是放在extension.xml中的ID,第二个是VibrationExtension的createContext所需要的参数。这个参数允许你创建不同的Extension Context,如果你只有一个context,native code中可以忽略这个参数。如果有多个extension context,你应该根据该参数创建不同的context。
if ( !extContext )
{
extContext = ExtensionContext.createExtensionContext("com.yourDomain.Vibrate","vibrate");
extContext.call("initMe");
}
3.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.
注意,你通过extContext.call调用initMe。这将导致Java代码中的VibrationInitFunction被调用。
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.
现在Extension Context已经被创建并初始化完成。还有两个函数需要实现。一个是isSupported,它将要连接native function isSupported。
1.Create a static getter called isSupported, which returns a Boolean:
该函数框架如下
public static function get isSupported():Boolean
{
var supported:Boolean = false;
// ...
return false;
}
2.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:
在上述代码中,加入以下代码,判断support
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:
用上述方法创建vibrateMe函数,如下
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:
Flash Builder会自动把你的库编译成SWC文件到你工程的bin目录。SWC文件是一个包含library.swf的archive。只要你用ADT打包一个ANE文件,你就要手动引用SWC和SWF。因此,现在你应该用解压缩工具打开SWC文件,解压出library.swf,把它放到bin目录下
- Navigate to HelloANELibrary/bin/.
- Unzip the HelloANELibrary.swc file, or open it in an archive management tool (e.g., 7-Zip).
- 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.)
从SWC文件中会看到catalog.xml和library.swf。library.swf需要被放置在每个目标平台的native code所在目录。比如,你需要把这个文件放入iOS、android、x86等。(可能有不同的library.swf的情况。。。) - When complete, your HelloANELibrary folder should contain both HelloANELibrary.swc, and HelloANENative should containlibrary.swf.
操作完成时,文件夹HelloANELibrary应该包含HelloANELibrary.swc,HelloANENative应该包含library.swf。(此话可能翻译不准确)
通过解压获得library.swf后,现在你已经可以创建一个native extension。注意,只要你改变了你的library 代码,你就需要重复这四步,否则library.swf会过时。
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).
为了创建一个ANE文件,首先,在Flex库工程中创建一个extension.xml文件,对于每个native target,。。。
Create the extension.xml file inside of your Flex library project:
- Right-click the src folder in your HelloANELibrary project. Click New > File.
- Name the fileextension.xml.
- Re-open this file by right-clicking it and selecting Text Editor, rather than the default XML editor.
建议用文本编辑器来打开 - 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):在标签 platform里,你可以在标签nativeLibrary中设定JAR的位置,并且设定Initializer的位置
<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
adt的位置 - Programming root directory: C:\Users\dan\Programs
- ActionScript 3 library directory: %root_directory%\HelloANELibrary
AS3库的目录 - Android native directory: %root_directory%\HelloANENative
Android native的目录 - Signing options: -storetype pkcs12 -keystore "c:\Users\dan\Programs\ cert.p12"
签名选项 - Destination ANE file: HelloANE.ane
目标ANE文件名 - Location of extension.xml: %library_directory%\src\extension.xml
extension.xml的位置 - Location of compiled ActionScript 3 library SWC: %library_directory%\bin\HelloANELibrary.swc
编译好的SWC的位置
你应该创建类似的脚本
"%adt_directory%"\adt -package %signing_options% -target ane "%dest_ANE%" "%extension_XML%" -swc "%library_SWC%" -platform Android-ARM bin/library.swf -C "%native_directory%" .
这个命令看起来有点复杂,不过它只是运行adt,并传入签名选项,指定ANE,提供extension名称,指定swc文件,目标平白,在哪里找native library文件
A compile_ane.bat file on Windows may look like this:
Windows平台上的批处理可能如下
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}" .
注意我使用p12文件作为我的签名证书。你可以用你通常使用的。如果你需要创建一个,最简单的途径就是打开一个Flex或者AIR工程,通过Project > Export Release Build,在第二步里会有机会通过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!
运行脚本,输入密码, %dest_ANE% 代表的文件应该生成。
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:
- In Flash Builder 4.5.2, click File > New > Flex Mobile Project.
创建一个Flex Mobile工程 - Name the project HelloANESample.
- Make sure you're using the 4.5.2 SDK or newer, and click Next.
确保使用4.5.2 SDK或者更新版本 - Make sure that only Google Android is selected as a target platform.
- Choose a View-Based application and click Next.
- Click Next, as you don't need any server settings.
- Click the NativeExtensions tab, which allows you to target any ANE files that your application needs.
迁移至Native Extensions页 - Click Add and browse to the ANE file you packaged in the previous step. It should be in the HelloANELibrary folder, named HelloANE.ane.
加入前面所打包的ANE文件 - 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.
- Click Finish.
- 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.
- Expand Flex Build Packaging and select Google Android.
- Click the Native Extensions tab.
- 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.
- Click OK to close the project properties.
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.)
- Right-click the HelloANESample-app.xml application descriptor, and choose to open it with the text editor.
- Scroll down to this section:
<android>
<manifestAdditions><![CDATA[
<manifest android:installLocation="auto">
3. Add permission to use the Vibration controls:
<uses-permission android:name="android.permission.VIBRATE"/>
Now that the project is configured, you can add a vibrate button to the home view:
- 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.
- 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();
3. 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);
4. Click the debug button in the main toolbar.
5. Select Google Android as the target platform, and choose to launch on the device, with debugging via USB.
6. 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.
- Coded in native Java to interface with the native extension APIs that Adobe AIR provides (FREObject, FREFunction, etc.).
- Coded a Flex library comprised of ActionScript 3 APIs. These hooked into your native APIs.
- Wrote an extension.xml describing our extension.
- Scripted a batch/bash file to package our native extension with the command line utility ADT.
- 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.
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).
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.
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.
Also be sure to check out the native extensions samples in the Adobe AIR Developer Center.