目录:
需求
预备知识
对AIR和ActionScript 3具有中等或深入的理解,熟悉Flash Builder和Java。
需要的产品
示例文件
开始之前
要充分掌握本教程中的知识,请确保您安装了以下软件:
- Flash Builder 4.6(注册获取预发行版*)
- Adobe AIR 3(包含在Flash Builder中),用于移动平台(AIR 2.5或更高版本,用于针对TV扩展的Adobe AIR)
- Adobe Flex 4.6(包含在Flash Builder中)
- Java API for AS扩展(包含在AIR 3 SDK中,位于lib/android/FlashRuntimeExtensions.jar下)
您还需要以下工具:
- 一个Android设备用于在设备上进行测试。
您可以选择使用另一种平台,但本指南的原生代码部分中的步骤将要求您维护您自己的原生构建环境。
- 正确安装的JDK和Android SDK。
其他资源
什么是原生扩展?
Adobe AIR允许应用程序开发人员使用一组称为Adobe AIR原生扩展的工具扩展运行时的功能。从2.5版开始,此功能已为AIR for TV启用,它现在已扩展到移动和桌面平台。通过使用原生扩展,您的应用程序可访问目标平台的所有功能,即使运行时本身没有内置的支持。
为了演示这一点,想象您在Android设备上创建一个应用程序,并希望在应用程序完成下载时震动电话。没有原生扩展支持,您要么必须使用Java编写整个程序,要么使用AIR并接受此任务不可实现的事实。但是,使用原生扩展,您可以创建一个横跨原生代码和您自己的应用程序逻辑的桥梁,允许您来回传递指令,使您的应用程序能够控制震动马达。您然后可以利用AIR的多平台支持将相同的应用程序部署到iOS,通过包含Objective-C代码来扩展您的原生扩展。您甚至可以将原生代码更改为可感知平台,以便更改震动持续时间和模式,无论应用程序是在Android上还是在iOS上运行。
原生扩展允许您利用您设备的独特和特定于平台的功能,它们还允许您在ActionScript应用程序中使用原生代码,重用现有的平台代码,在线程中执行操作来提高您应用程序的处理能力,以及提供对原生平台库的访问。原生扩展的封装和分发就像所有其他ActionScript库一样:您可以分发您自己的库,以及使用其他开发人员发布的原生扩展,可以将功能插入到您自己的应用程序中。
Adobe还提供了多个经过良好备案的原生扩展示例,将帮助开发人员开始使用上述功能。
入门
本教程将指导您开始自行创建一个原生扩展。它将介绍创建用于Android的原生Java代码、ActionScript 3代码和一个原生扩展文件所需的步骤,您还将学习如何创建一个使用您的原生扩展的Flex移动应用程序,最后您将在您的设备上测试它。尽管这是一个"Hello, World!"教程,但我们会避免通过原生代码打印这条常见的消息,而选择控制Android智能电话的震动马达。如果您感觉这有点冒险(或者希望针对不同的平台),您可以选择调整本指南的原生代码部分来适应非Android平台。
以下是以下页面中将采取的总体步骤:
创建原生代码
- 在Flash Builder 4.6中设置一个Android开发环境。
- 连接和测试您的Android设备。
- 创建一个原生Android项目。
- 创建Java代码来创建一个扩展上下文。
- 创建Java代码来连接回ActionScript 3。
- 编译您的代码。
创建ActionScript 3代码
- 在Flash Builder 4.6中设置一个ActionScript 3库项目。
- 创建与您的Java代码连接的桥。
- 为您的Android电话设置一个Flex移动电话。
创建.ANE文件,将原生代码和ActionScript 3捆绑在一起
- 处理证书。
- 使用adt命令。
- 修改您的Flex应用程序以使用新的.ANE文件。
测试您的原生扩展
证明您的原生扩展有效!
创建原生代码
原生扩展的核心是原生代码,它充当着您的应用程序与您希望控制的功能之间的第一层。在本示例中,原生代码将控制震动马达,使用ActionScript 3发送和接受信息。您将自行编写功能来控制震动马达,还将使用Adobe AIR提供的Java API来向ActionScript 3发送和从其接受数据。
但是,在开始编码和使用库之前,您必须设置您的构建环境。
使用Flash Builder 4.6设置一个Android开发环境
因为您将编写原生Android代码,您需要一个可操作Android SDK的开发环境。如果您还没有,请依照本指南开头包含的链接安装和配置Android SDK。完成此过程后,您将修改Flash Builder以创建和编译Android项目:
- 打开Flash Builder并转到Install New Software(在OS X上为Flash Builder > Window > Install New Software)。(如果有必要,关闭并使用管理特权重新启动Flash Builder。)
- 在Work with框中输入Google的公共ADT软件存储库的路径:https://dl-ssl.google.com/android/eclipse/。单击Add并将存储库命名为Google ADT。单击OK添加存储库。
- Flash Builder将下载一组可用软件。安装所有开发人员工具,包括Android DDMS、Android开发工具、Android历史查看器和Android Traceview。单击Next两次,阅读EULA并决定是否同意,等待包安装。一些Android包包含未签名的内容,即使这样您也应该继续安装。
- 在提示时重新启动Flash Builder。
- 您需要将Flash Builder指向您的Android SDK的位置。转到Window > Preferences(在OS X上为Flash Builder > Preferences)并单击Android。单击Browse并选择您的Android SDK的根位置(比如C:\Users\dan\Programs\android-sdk)。如果您缺少Android SDK的任何组件,您将收到一个指令要求运行Android SDK管理器并安装它们。选择最新版本的SDK并单击OK。
- 单击Window > Android SDK and AVD Manager。
- 转到Available Packages并选择最新的Google Inc.包,以确保您拥有最新的Android SDK和API包。您可能还希望安装USB驱动程序,具体取决于您的设备。
- 关闭此窗口。您现在应该能够单击File > New Project,查看并输入Android Project的信息。完成之后,Flash Builder就已正确配置。
连接并测试您的Android设备
您可能急于了解编写代码的具体细节,但如果现在确认您的电话能够连接并经过了Android SDK的识别,将在以后省掉不少麻烦。
- 连接您的Android设备
- 在设备上,转到Settings > Applications > Development并确保勾选了"USB Debugging"复选框,确认开发模式已启用。
- 如果有必要,单击操作您计算机上允许安装驱动程序的对话框。在Windows上,您可能希望选择位于android_sdk\usb_driver文件夹中的驱动程序。
- 打开一个命令提示符或终端,运行"adb devices"。
- 您应该看到列出了您的设备ID。如果没有,请查阅在线指南,使adb可处理您的电话。如果该命令生成"command not found"或类似错误,请将android_sdk\tools文件夹的位置添加到您的系统PATH变量,并再试一次。
创建一个新Android项目
我们现在必须在Flash Builder中创建一个新Android项目,要求链接器查找随AIR SDK提供的Java API JAR文件。
- 在Flash Builder中,转到File > New > Project。
- 选择Android Project并单击Next。
- 将项目命名为HelloANENative。
- 标注项目位置(如果愿意,您也可以更改该位置)。
- 确保为您的构建目标选择了一个最新的Android SDK。如果没有看到您想要的目标,返回到前面的步骤以将Android SDK安装在Eclipse中,或者更新SDK以包含您想要的API和目标。本教程的剩余部分将假设您选择了Android 2.3.3或更新版本,但这应该不会影响下面的任何指令。
- 输入包名称com.<您的域>.example.android.helloANE。例如com.yourDomain.example.android.helloANE。
- 单击Finish。
- Flash Builder将为您创建一个HelloANENativeActivity.java文件。使用包资源管理器打开它。
- 在包资源管理器中右键单击HelloANENative项目,单击Properties。
- 单击Java Build Path,然后单击Libraries。
- 您现在必须添加Java API,以便连接ActionScript 3。单击Add External JARs。
- 找到并选择FlashRuntimeExtensions.jar文件(该文件的路径类似于:C:\...\Adobe Flash Builder 4.5\sdks\4.5.2\lib\android\)
- 单击OK解除项目属性对话框。
您还有一项项目配置任务:设置应用程序,以拥有使用设备的震动控件的权限。
- 打开AndroidManifest.xml文件。您应该看到Android Manifest Permissions屏幕。
- 单击Add并选择Uses Permission,然后单击OK。
- 在Attributes for Uses Permission下,选择android.permission.VIBRATE。
- 选择HelloANENative Manifest文件。
您已执行了这一步,所以如果您决定创建原生测试案例,您的原生代码将拥有运行所需的必要权限。尽管本教程没有介绍它,在继续编写ActionScript 3之前测试原生代码可能很有帮助——尤其是对于更高级的原生扩展。
什么是扩展上下文?
现在您的Android项目已正确配置,您必须开始添加在ActionScript和您的原生Java代码之间建立桥梁的结构。这些结构中的第一个是一个扩展上下文。扩展上下文负责包含最多3个基于原生扩展的项目(另请参见Oliver Goldman的文章Extending Adobe AIR*):
- 原生函数到ActionScript可饮用的名称的映射。这允许ActionScript代码调用特定的原生函数,是原生扩展的核心部分。
- 一个ActionScript对象的引用,它在原生代码与您的AIR应用程序之间共享。
- 原生代码结构的引用,它仅可从原生代码访问。
现在您的原生扩展可以拥有多个扩展上下文,您应该基于功能将它们分开。在本例中,您仅需要一个上下文,它将提供一个映射来访问Android震动功能。
创建一个震动扩展上下文
接下来您将创建一个新VibrationExtensionContext类。
- 在包资源管理器中,右键单击src.com.yourDomain.example.android.helloANE包并选择New > Class。
- 将包设置为com.yourDomain.example.android.helloANE.extensions。
- 将名称设置为VibrationExtensionContext。
- 将超类设置为com.adobe.fre.FREContext。您可以使用Browse按钮选择此类。这是Adobe提供来使原生扩展桥生效的AIR Java API。
- 单击Finish创建新类。
您将看到已为您创建了两个函数:public void dispose()和public Map<String, FREFunction> getFunctions()。您可能已猜到,getFunctions()必须返回字符串(在您的ActionScript 3代码中引用和任何FREFunction类(您接下来将定义)之间的一个键值对映射。Adobe提供的API为您提供了以缩写词FRE(表示Flash运行时扩展)开头的类和函数。
创建函数映射
在您的原生扩展中定义函数的第一步是创建一个新Map用于返回。在getFunctions()类中,添加:
- @Override
- public Map<String,FREFunction> getFunctions()
- {
- Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
- return functionMap;
- }
这创建一个空HashMap,但它如果是空,显然不是很有用。您将映射3个键值对,每个键值对将定义一个实现FREFunction接口的类:
- 将isSupported映射到一个VibrationSupportedFunction。这将运行某种逻辑,将一个FREObject传递到包含true或false(具体取决于平台是否支持震动)的ActionScript 3。
注意:一种最佳实践是在使用您的原生扩展的其他功能之前,始终允许ActionScript 3代码执行兼容性检查。
- 将vibrateMe映射到一个VibrationVibrateFunction。这个FREFunction接受一个来自ActionScript 3的参数,以控制震动的持续时间,然后在您的设备上执行实际的震动。
- 将initMe映射到一个VibrationInitFunction。在其他函数准备好被使用之前,此函数允许原生代码执行和初始化任务。在本例中,您将创建Android的VIBRATOR_SERVICE的引用,它将用在VibrationVibrateFunction(在ActionScript 3中也称为"vibrateMe")中。
- 在getFunctions()类中,在您的functionMap对象上调用put()函数。第一个参数将是一个字符串,表示上述函数名称,第二个函数是该函数(还未创建)的一个新实例:
- functionMap.put("initMe", new VibrationInitFunction());
- functionMap.put("isSupported", new VibrationSupportedFunction());
- functionMap.put("vibrateMe", new VibrationVibrateFunction());
三个函数中的第一个:VibrationInitFunction
您已定义了三个函数。接下来,您将它们编写为实现FREFunction接口的类。您将从VibrationInitFunction开始,它被调用时将在您的VibrationExtensionContext中设置一个将在以后用于震动设备的类。
- 右键单击包资源管理器中的一个包,选择New > Class。
- 将包设置为 com.yourDomain.example.android.helloANE.extensions,将名称设置为VibrationInitFunction。
- 保留Superclass为一个java.lang.Object,向Interfaces框添加一个新条目:com.adobe.fre.FREFunction。
- 单击Finish。
请注意,你可以通过使用Eclipse的代码生成功能完成以上的步骤:点击你希望创建的类(本例中是VibrationInitFunction),敲击Ctrl –1打开代码提示窗口,然后选择 "创建VibrationInitFunction类 "。这样编辑器就可以自动帮你创建类代码了。
您将看到,已在您的VibrationInitFunction中为您定义了一个函数:call(),它接受两个参数:一个FREContext和一个FREObject[]数组。默认情况下,这两个参数定义为arg0和arg1,但您可以为它们提供更具描述性的名称。将call()函数定义更改为类似以下形式:
- public FREObject call(FREContext context, FREObject[] passedArgs)
当调用此函数时,第一个参数将是您的VibrationExtensionContext的引用,第二个参数将是ActionScript 3代码传递的所有参数(如果有)的数组。这将对您的VibrationVibrateFunction很重要,它将基于该数组中的第一个参数设置持续时间。
现在,您的init函数将使用传入的FREContext对象来获取VibrationExtensionContext,然后获取它所属的Android活动。使用此活动引用,它然后将能够检索名为Context.VIBRATOR_SERVICE的全局Android系统服务,该服务将允许您控制震动马达。您将此系统服务存储在您的VibrationExtensionContext中包含的一个新变量中,您稍后将创建它:
- 在VibrationInitFunction的call()函数中,添加以下代码来从传入的FREContext获取VibrationExtensionContext:
- VibrationExtensionContext vbc = (VibrationExtensionContext)context;
- 您现在可以使用VibrationExtensionContext getActivity()函数抓取活动。在FREContext类中包含此函数的目的是为了支持常见任务,比如您需要抓取上下文的活动,进而拥有我们需要的SystemService的路径。
- Activity a = vbc.getActivity();
- 您现在可以调用a.getSystemService(),传入全局Context.VIBRATOR_SERVICE的引用。这将返回一个类型为Vibrator的对象。您需要一个可用于整个扩展上下文的位置来存储此引用,所以将它放在一个位于VibrationExtensionContext内的新变量vb中。
- vbc.vb = (Vibrator)a.getSystemService(Context.VIBRATOR_SERVICE);
- 您现在应该打开VibrationExtensionContext类,向该类添加一个名为vb的公共变量:
- public Vibrator vb = null;
现在,您已创建了一个原生代码结构的引用vb Vibrator类,它可供可引用您的VibrationExtensionContext的任何类访问。
您完成的VibrationInitFunction看起来应该类似于:
- 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;
- }
- }
您已学习了如何:创建一个实现FREFunction的类,理解从ActionSscript 3传入的参数,通过FREContext参数传入您的扩展上下文,而且您已看到扩展的一种常见的初始化任务。
接下来您必须实现在Map<String, FREFunction> getFunctions()函数中实现的其他两个FREFunction。
三个函数中的第二个:VibrationSupportedFunction
您前面定义的第二个函数是VibrationSupportedFunction。您在getFunctions()返回的HashMap中已表明,此函数可使用ActionScript 3字符串isSupported调用。此函数的创建非常类似于VibrationInitFunction,但它将向您展示如何在一个FREObject内返回一个布尔值。
- 右键单击包资源管理器中的一个包,单击New > Class。
- 选择com.yourDomain.example.android.helloANE.extensions作为包,将此类命名为VibrationSupportedFunction,正如前面的HashMap中提供的值所期望的。
- 选择FREFunction作为此类将实现的一个接口。
- 单击Finish创建该类。
- passedArgs, respectively.
将此类的参数分别从arg0和arg1更改为context和passedArgs。 - 您将希望返回一个FREObject作为call()函数中的结果,现在创建并返回该结果。您还想要您的VibrationExtensionContext的引用,所以通过转换上下文参数来创建它:
- FREObject result = null;
- VibrationExtensionContext vbc = (VibrationExtensionContext)context;
- // ...
- return result;
- 此函数的逻辑将如下所示:
- VibrationInitFunction函数已调用,因此应该设置vbc.vc。
- vbc.vc是否非空?如果非空,结果应该为true。
- 如果vbc.vc为空,我们可以合理地推断初始化失败,以及此平台不支持震动。结果应该设置为false。
创建以下if语句:
- if (vbc.vb == null)
- {
- result = FREObject.newObject(false);
- }
- else
- {
- result = FREObject.newObject(true);
- }
- 还有一项任务:在FREObject上调用newObject()可能导致抛出一个FREWrongThreadException异常。您会将您的if语句放在一个try catch代码块中,以处理此不测事件。
您完成的call()函数现在看起来应该类似于:
- @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;
- }
您现在有了3个原生扩展函数中的第二个:VibrationSupportedFunction。当被ActionScript 3字符串isSupported调用时,此函数将检查VibrationExtensionContext"context"中的变量vb是否为非空。它将基于此条件返回值为true或false的FREObject,将捕获一个可能由FREObject的静态newObject()函数抛出的FREWrongThreadException。
三个函数中的第三个:VibrationVibrateFunction
您必须实现的最后一个原生扩展函数执行您的原生扩展的核心职责:它允许AIR应用程序震动设备的马达指定的持续时间。
- 在包资源管理器中,右键单击一个包并选择New > Class。
- 选择com.yourDomain.example.android.helloANE.extensions作为包,将类命名为VibrationVibrateFunction。
- 将类实现命名为com.adobe.fre.FREFunction。
- 单击Finish创建该类。
- 在函数定义中,将arg0重命名为context,将arg1重命名为passedArgs。
- 创建一个名为result的空FREObject。
- 将上下文变量转换一个名为vbc的VibrationExtensionContext变量。您将使用此变量访问Vibrator对象vbc.vb。
我们现在已准备好访问第一个传入的参数FREObject,尝试将它设置为一个整数。如果数据格式奇怪,可能会抛出一个异常并且您将获取该异常。您的call()函数现在看起来应该类似于:
- @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;
- }
- 在try { // ... }代码块内,我们将尝试抓取passedArgs数组中的第一个元素作为FREObject:
- FREObject fro = passedArgs[0];
- 我们现在可以在这个FREObject上调用getAsInt();来创建一个int:
- int duration = fro.getAsInt();
- 最后,在我们的vb Vibrator变量上调用Android原生震动函数,传入持续时间:
- vbc.vb.vibrate(duration);
您现在已成功创建了3个原生函数,将它们映射到了getFunctions()提供的HashMap中的字符串,创建执行您的原生扩展所需的所有操作所必要的原生逻辑。这样就完成了VibrationExtensionContext的创建,它是您的原生扩展需要的唯一的扩展上下文。
创建主要扩展类
您已创建了您的原生扩展需要的一个且是唯一一个扩展上下文,但您还未创建我们的扩展的主要类。幸运的是,添加此类很简单,我们所需做的就是创建一个名为VibrationExtension的类,它实现FREExtension接口。
FREExtesion接口定义initialize、dispose和createContext函数,它们允许挂钩到一个原生扩展的生命周期中。尽管为我们提供了3个函数,我们仅需要自定义一个:createContext函数。此函数必须返回一个FREContext。幸运的是,您已创建了您自己的VibrationExtensionContext,可以简单地返回此类的一个实例。
- 右键单击一个包,选择New > Class。
- 选择com.yourDomain.example.android.helloANE.extensions作为包名称。
- 将类命名为VibrationExtension。
- 使用接口框旁边的Add按钮添加类实现com.adobe.fre.FREExtension。
- 单击Finish创建该类。
- 默认情况下,createContext()函数中定义的字符串参数将显示为arg0。此参数实际上是一个ID,定义要创建的上下文类型(它仅在您拥有多种上下文类型时才有用)。将arg0更改为contextType。
- 要完成createContext()函数,我们仅需要返回VibrationExtensionContext的一个新实例。将return null;代码替换为以下代码:
- return new VibrationExtensionContext();
这将初始化并创建您的扩展上下文,允许您使用放在您的原生扩展内的原生代码。
将您的原生代码导出为JAR文件
在本教程的以下各节中,我们将介绍如何编写原生扩展的ActionScript 3部分代码,以及封装和测试完成的原生扩展文件和示例应用程序。这些步骤将涉及以JAR文件的形式引用您的原生代码。在Flash Builder中创建一个JAR文件很简单:
- 在包资源管理器中选择您的HelloANENative项目之后,转到File > Export。
- 选择Java > JAR file并单击Next。
- 选择HelloANENative作为要导出的资源。
- 确保选择了"Export generated class files and resources"。
- 选择JAR文件的目标,将它命名为HelloANENative.JAR并单击Finish。您将在您的Flex库项目中创建extension.xml文件时,以及运行封装命令来创建您的原生扩展文件时,使用此JAR文件。
创建ACTIONSCRIPT 3代码
您已完成了在创建原生扩展过程中大部分必要的编码工作,创建了可使用其他函数、逻辑以及(如果有必要)甚至其他扩展上下文进行扩展的Java代码,以扩大您的原生扩展的范围。
相对而言,创建完成此平台桥所需的ActionScript 3代码比较简单。您的任务包括:
- 创建和配置一个Flex库项目。
- 创建一个extension.xml文件来描述您的扩展。
您的ActionScript 3库代码将包括一个类,该类将导入flash.external.ExtensionContext API,提供以下函数:
- 一个构造函数,它将创建一个具有合适ID的新扩展上下文,还将调用您的initMe原生函数。
- 一个名为isSupported的函数,它将调用我们的isSupported原生函数,还将依据来自我们的原生代码的响应而返回true或false。
- 一个名为vibrate的函数,它将接受一个表示持续时间的数字,使用此数字作为参数调用您的原生vibrateMe函数。
- 完成此代码后,您的ActionScript 3库函数就完成了,您可以继续封装和使用您的原生扩展。请注意,库函数定义了使用您的原生扩展所必要的ActionScript 3 代码,但它不是一个示例应用程序。为了使用您的原生扩展,您将必须从一个Flex移动应用程序引用这个库应用程序,在本指南后面一节中将创建该移动应用程序。
创建一个Flex库项目
您的ActionScript 3 代码将位于一个Flex库项目中:
- 在Flash Builder中,在屏幕右上角选择Flash perspective打开Flash透视图。
- 单击File > New > Flex Library Project。
- 将项目命名为HelloANELibrary。
- 确保选择了Generic library单选按钮。
- 确保勾选了"Include Adobe AIR libraries"。您的项目将依赖的原生扩展库包含在AIR API中。
- 单击Finish创建项目。
- 在您的包资源管理器中打开HelloANELibrary项目,右键单击src文件夹,选择New > ActionScript Class。
- 将包命名为com.<您的域>.nativeExtensions.Vibrate。
- 将类命名为Vibrate。
- 将超类设置为flash.events.EventDispatcher。这将允许此类分派事件,这在您将原生扩展集成到真实应用程序中时很有用。
- Click Finish to create the Vibrate class.
向您的原生代码编写ActionScript 3桥
您现在必须创建与我们的扩展上下文的连接,这将允许您访问您使用Java创建的initMe、isSupported和vibrateMe。
- 在Vibrate类中,添加一个扩展上下文的一个私有、静态的引用:
- private static var extContext:ExtensionContext = null;
- 在构造函数中,您将验证这个extContext变量是否已初始化。如果没有,您将调用静态函数ExtensionContext.createExtensionContext(),传入两个标识符。第一个是一个ID,您稍后将在一个extension.xml文件中设置。第二个是一个参数,传递到VibrationExtension的createContext()函数。您应该记得,它允许您创建不同的扩展上下文,因为您只有一个,所以在原生代码中忽略了此阐述。如果您拥有多个扩展上下文,您应该让原生代码使用if或switch语句分析您传入的值,基于可用的共享字符串值创建合适的值。编写以下代码:
- if ( !extContext )
- {
- extContext = ExtensionContext.createExtensionContext("com.yourDomain.Vibrate","vibrate");
- extContext.call("initMe");
- }
- 请注意,您通过extContext.call()调用了initMe,没有传入其他参数。这将这将使用您使用Java编写的VibrationInitFunction完成,将为您初始化震动设备所必要的内部数据结构。
现在,只要任何使用您的新ActionScript 3库的应用程序调用Vibrate()构造函数,就会创建和初始化您的扩展上下文。但是,您还有两个函数要实现。首先创建isSupported()函数,它将连接到原生isSupported函数,并检查您的应用程序逻辑所返回的布尔值。
- 创建一个名为isSupported的静态getter,它返回一个布尔值:
- public static function get isSupported():Boolean
- {
- var supported:Boolean = false;
- // ...
- return supported;
- }
- 在两个语句之间,添加一个对extContext.call()的调用,传入isSupported作为一个字符串参数,这会将您的supported变量设置为返回的布尔值:
- supported = extContext.call("isSupported") as Boolean;
-
重复此过程以创建vibrateMe函数,它将接受一个Number作为持续时间。此函数的创建很简单:
- public function vibrate(duration:Number):void
- {
- extContext.call("vibrateMe",duration);
- }
请注意,Flash Builder会自动将您的库编译为一个SWC文件(位于项目的bin文件夹中。SWC文件是一个包含library.swf的压缩文件。只要您使用ADT封装一个ANE文件,就必须手动引用SWC和SWF。因此,您现在应该在压缩文件管理工具中打开SWC文件,提取library.swf,将它放在HelloANELibrary的bin/目录中:
- 导航到HelloANELibrary/bin/。
- 解压HelloANELibrary.swc文件,或者在压缩文件管理工具(比如7-Zip)中打开它。
- 您将在SWC压缩文件中看到一个catalog.xml和一个library.swf文件。
library.swf文件需要放在您针对的每个平台的原生代码目录内。例如,可以将此文件放在了iOS/、android/、x86/等目录内,具体取决于您的项目的目标平台。(对于更高级的ANE,您可以指定不同的library.swf的文件,只要您需要您的AS3库对于不同平台而言不同。但是,这与定义一个通用接口的最佳实践不符,建议您坚持使用单一版本的library.swf。)
- 完成时,您的HelloANELibrary文件夹应该包含HelloANELibrary.swc,HelloANENative应该包含library.swf。
通过提取library.swf,您现在拥有创建原生扩展所需的所有文件。请注意,只要更改了您的库代码,就必须重复步骤1到4,否则library.swf将过期。
现在您编写了使用原生扩展所需的所有库代码。
在extension.xml文件中描述您的原生扩展
您创建了必要的代码,但还未将所有内容链接到一个ANE文件中。首先在您的Flex库文件内创建一个extension.xml文件。对于每个原生目标,此文件指向原生代码(您的JAR文件),并描述初始化器函数(以及可选地,一个终结器函数,本示例中不需要它)的包位置。在创建您的ANE文件(后面将在一个示例应用程序中使用它)时,将此文件传递给封装程序。
在您的Flex库项目中创建extension.xml文件:
- 右键单击您的HelloANELibrary项目中的src文件夹。单击New > File。
- 将文件命名为extension.xml。
- 右键单击此文件并选择Text Editor,而不是默认的XML编辑器,以重新打开此文件。
- 以下XML描述了要使用的AIR命名空间(2.5)、扩展的ID,以及我们希望针对的单一平台(请注意"iPhone-ARM"是另一个通用的目标平台):
- <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>
-
在<platform>标记内,现在将设置JAR文件在一个<nativeLibrary>标记中的位置,将初始化器的位置设置为我们在原生代码中设置的位置(回想一下,您在VibrationExtension类中创建了initialize()函数)。
- <applicationDeployment>
- <nativeLibrary>HelloANENative.jar</nativeLibrary>
- <initializer>com.yourDomain.example.android.helloANE.extensions.VibrationExtension </initializer>
- </applicationDeployment>
您现在已成功创建了您的extension.xml文件,拥有了创建ANE文件所需的所有组件。
封装原生扩展
目前,封装一个原生扩展需要使用命令行工具adt,向它传递一些参数。我建议在Windows中创建一个批处理脚本(.bat文件),或者在OS X中创建一个bash脚本(通常为.sh文件),您将创建的脚本将允许您在脚本的顶部设置您自己的变量,支持针对您的其他原生扩展项目轻松调整它。
您需要将许多信息插入到脚本中。我将列出这些信息,给出我在我自己的系统上使用的值:
- adt的位置:C:\Program Files\Adobe Flash Builder 4.5\sdks\4.5.2\bin
- 编程根目录:C:\Users\dan\Programs
- ActionScript 3库目录:%root_directory%\HelloANELibrary
- Android原生目录:%root_directory%\HelloANENative
- 签名选项:-storetype pkcs12 -keystore "c:\Users\dan\Programs\ cert.p12"
- 目标ANE文件:HelloANE.ane
- extension.xml的位置:%library_directory%\src\extension.xml
- 编译的ActionScript 3库SWC的位置:%library_directory%\bin\HelloANELibrary.swc
您应该为您自己的系统创建一个类似的值列表。您然后可以使用以下ADT命令引用变量来插入它们。
- "%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.xml文件,指定HelloANELibrary.swc文件,以Android-ARM作为目标平台,以及高速ADT在何处查找原生库文件。
Windows上的compile_ane.bat文件看起来可能类似于:
- 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%" .
在Mac OS X上,该脚本可能类似于:
- #!/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文件作为签名证书。您可以使用您通常用于签名AIR文件的文件代替。如果您需要创建一个,最简单的方式是在Flash Builder中打开一个Flex或AIR项目,转到Project > Export Release Build。在第二步,您将可以选择使用GUI创建一个证书。
从命令行运行您的脚本,输入您证书的密码,应该还会创建%dest_ANE%文件,它可用于示例应用程序中!
在FLEX示例应用程序中使用原生扩展
您现在将创建一个使用您的原生扩展的Flex移动应用程序!设置一个项目并将它部署到Android的流程很简单:
- 在Flash Builder 4.5.2中,单击File > New > Flex Mobile Project。
- 将项目命名为HelloANESample。
- 确保您使用了4.5.2或更高版本的SDK,单击Next。
- 确保仅选择了Google Android作为目标平台。
- 选择一个基于视图的应用程序,单击Next。
- 单击Next,因为您不需要任何服务器设置。
- 单击NativeExtensions选项卡,它将允许您找到您的应用程序需要的任何ANE文件。
- 单击Add并浏览到您在上一步中封装的ANE文件。它应该位于HelloANELibrary文件夹内,名为HelloANE.ane。
- 单击OK,您应该在ANE文件的条目旁边看到一个绿色的勾选符号。如果展开此条目,应该会看到以下警告"Launching on win32(Windows-x86) is not supported"(或针对OS X的类似消息)。这是因为,我们在编写原生扩展的元神代码时,没有以我们的桌面环境为目标,在编写extension.xml或运行ADT时也没有配置它。
- 单击Finish。
- 现在再次检查确认原生扩展已包含在封装的文件中,而不只是包含在构建路径中。尽管它应该已正确配置,但如果您修改了原生扩展,可能必须执行这些步骤。在包资源管理器中右键单击您的项目,选择Properties。
- 展开Flex Build Packaging并选择Google Android。
- 单击Native Extensions选项卡。
- 您应该在原生扩展旁边看到一个绿色勾选符号,以及在针对该原生扩展的Package列中看到一个勾选符号。如果没有,请选择该复选框。
- 单击OK关闭项目属性。
配置权限
要完成您项目的设置,需要指定您的应用程序需要使用Android震动控件。在利用设备的其他功能时,请特别注意这个方面——很容易忘记某些功能需要其他权限。AIR应用程序描述符不会使这些条目可用于注释掉的部分中,因为正在使用运行时的功能。如果您忘记了指定合适的权限,原生代码将无法工作,可能将抛出一个与权限相关的异常。(在Android上,此输出可在adb shell中使用logcat轻松看到。
要向AIR应用程序描述符添加权限:
- 右键单击HelloANESample-app.xml应用程序描述符,选择使用文本编辑器打开它。
- 向下滚动到这一节:
- <android>
- <manifestAdditions><![CDATA[
- <manifest android:installLocation="auto">
- 添加使用震动控件的权限:
- <uses-permission android:name="android.permission.VIBRATE"/>
使用您的原生扩展
现在项目已配置,您可以向主视图添加一个震动按钮:
- 在s:View标记(类的主体中)之间,添加一个新s:Button。为它提供一个标签Vibrate using ANE,创建一个新的单击处理函数。Flash Builder应该自动为您创建一个fx:Script标记和ActionScript 3单击处理函数。
- 在您的单击处理函数内创建一个新Vibrate类,它将通过一个ActionScript 3对象公开您的原生扩展:
- var v:Vibrate = new Vibrate();
- 跟踪v.isSupported的值,然后调用您的主要震动函数,传入硬编码的值100作为马达应该运行的毫秒数:
- trace("Is this ANE supported on this platform? " + Vibrate.isSupported);
- v.vibrate(100);
- 单击主要工具栏中的调试按钮。
- 选择Google Android作为目标平台,选择启动设备,通过USB进行调试。
- 单击Debug。
Flex应用程序现在应该启动设备,使用ANE提供一个标为Vibrate的按钮。点击此按钮应该会产生来自您的Android设备中的马达的持续100ms的震动!您还将在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
如果您希望控制震动持续时间,可以添加一个TextInput或数字输入表。只需将我们硬编码的参数100替换为一个全局范围的变量,使用一个控件来设置此变量。现在,编写应用程序的ActionScript 3代码与其他Flex应用程序开发没什么两样。
延伸阅读
在本指南中,您了解到AIR原生扩展允许扩展Adobe AIR的功能,为您的应用程序提供访问设备和硬件功能的能力,这些功能无法单独通过运行时API访问。您学习了如何为Android创建原生扩展,可以将这些技能用于其他目标平台。在本例中,您重点学习了使Android设备的震动马达激活指定的持续时间的简单任务,这个示例演示了如何创建并初始化一个原生扩展,以及在您的原生代码和AIR应用程序之间来回传递数据。
为了完成此任务,您:
- 编写了原生Java代码来连接Adobe AIR所提供的原生扩展API(FREObject、FREFunction等)。
- 编写了一个包含ActionScript 3 API的Flex库。这些API挂钩到您的原生API中。
- 编写一个描述我们的扩展的 extension.xml。
- 编写一个 batch/bash 文件,使用命令行实用工具 ADT 封装我们的原始扩展。
- 创建一个使用原生扩展的移动Flex项目。
- 尽管您重点关注的是Android,原生扩展也适用于iOS、Mac OS X、Windows和Adobe AIR for TV。您可以创建针对多个平台的单一原生扩展,您的应用程序逻辑可以按平台确定(在运行时或编译时)具体的功能是否受支持。
- 您现在拥有了创建您自己的原生扩展所必要的知识和技能。您的应用程序可访问更多硬件功能,利用原生优化的代码或第三方库,甚至生成多个线程来处理耗时的计算,而不影响您的AIR应用程序的运行。
在原生扩展中包含资产
我们的示例不需要除已编译代码外的任何资产,但是您可能希望您的原生扩展能够访问图像、文件、数据库或配置存储等。这很可能发生,并且需要注意一些移动方面的考虑因素:
- 在Android上,将您的资产包含在Android项目路径的"res"文件夹中。这些文件将合并到您的主要应用程序的资源目录中,所以您需要选择不会与其他资产冲突的唯一名称。要访问它们,您可以使用FREContext.getResourceId(),传入想要的资源ID(另请参见Oliver Goldman的文章"扩展Adobe AIR*")。
- 在iOS上,资源通过NSBundle API来访问,请注意,在编译项目时命名空间会扁平化,您为资源选择的名称(即使仅在原生代码中使用)应该保证不会与您项目中的其他资源冲突。例如,不要在您项目中的任何地方使用两个都名为Localizable.strings的资源(另请参见Oliver Goldman的文章"扩展Adobe AIR*")。
分派状态事件
您可能将会发现,您的原生扩展必须在原生代码中执行异步任务,您将需要一种方式来在任务完成时将通知传递给您的AIR应用程序。这通过函数FREContext类中的函数dispatchStatusEventAsync(String code, String level);来完成。例如,以下Java代码告诉一个虚构的原生扩展库,有一个编码为"DATA_CHANGED"的状态事件:
- context.dispatchStatusEventAsync("DATA_CHANGED", stringData);
- 此状态事件将异步分派,并且(假设您的AIR应用程序不是很忙)将立即可供相应的原生扩展ActionScript 3事件监听器使用。因为上下文可以分派这些事件,所以您将必须要求原生扩展库监听它们:
- context.addEventListener(StatusEvent.STATUS, onStatus);
- ...
- private function onStatus(e:StatusEvent):void
- {
- if (e.code == "DATA_CHANGED")
- {
- var stringData:String = e.level;
- // ...
- }
- }
状态事件提供了一种方便的方式来在原生代码任务的状态上更新您的原生扩展库(进而更新您最终的AIR和Flex应用程序)。
了解更多信息
您可以阅读本指南开头的"其他资源"一节中的内容,继续了解原生扩展的知识。这些资源包括Adobe已创建和分发的原生扩展的链接,允许您通过将原生扩展文件放在您的Flex和ActionScript 3应用程序中而扩展AIR的功能。
另外,一定要查阅Adobe AIR开发人员中心中的原生扩展示例*。
关于作者
Daniel Koestler 是 Adobe 的一名应用程序开发人员,他使用 Adobe AIR、Flex 和 Flash Builder。他从詹姆斯麦迪逊大学毕业,主修计算机科学和天文学。作为一名应用程序开发人员,Dan 有机会在 Adobe 内部和外部创建真实的应用程序,并借助这些经验为负责 AIR 2、Flex 4 和 Flash Builder 的小组提供反馈。他曾帮助创建 AIR 新闻阅读器 ShareFire,近期在 Adobe MAX 2009 上探讨了 AIR 和 Flex 中的辅助功能,演示一个可供有视觉障碍的用户使用的简单的 Twitter 客户端。Daniel 通过他的博客*和 Twitter* 讨论 AIR、Flex 和 Flash。