目录
需求
预备知识
对 AIR 和 ActionScript 3 具有中等或深入的理解,熟悉 Flash Builder 和 Java。
用户水平
中级
需要的产品
示例文件
备注:Adobe建议使用下一版Flash Builder来为Adobe AIR开发原生扩展。立即注册访问 Flash Builder 4.6的预发行版。
本教程向您使用简单的代码为 Adobe AIR 创建您自己的原生扩展。除了向您展示在所有原生扩展(AIR、ActionScript 3 和 Flash Builder)中通用的代码和技能,本教程还介绍了在 Android 平台上编译原生代码。您可能对使用 Objective-C、C#、C++、C 或其他某种语言进行开发感兴趣,本文的 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。
其他资源
- Android SDK
- 安装 Android SDK
- 为 Adobe AIR 开发 ActionScript 扩展
- 将 Android 开发工具(ADT)添加到 Flash Builder 4.5.1
什么是原生扩展?
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());
3 个函数中的第一个:VibrationInitFunction
您已定义了 3 个函数。接下来,您将它们编写为实现 FREFunction 接口的类。您将从 VibrationInitFunction 开始,它被调用时将在您的 VibrationExtensionContext 中设置一个将在以后用于震动设备的类。
- 右键单击包资源管理器中的一个包,选择 New > Class。
- 将包设置为 com.yourDomain.example.android.helloANE.extensions,将名称设置为 VibrationInitFunction。
- 保留 Superclass 为一个 java.lang.Object,向 Interfaces 框添加一个新条目:com.adobe.fre.FREFunction。
- 单击 Finish。
您将看到,已在您的 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;
3. 您现在可以使用 VibrationExtensionContext getActivity() 函数抓取活动。在 FREContext 类中包含此函数的目的是为了支持常见任务,比如您需要抓取上下文的活动,进而拥有我们需要的 SystemService 的路径。
Activity a = vbc.getActivity();
5. 您现在可以调用 a.getSystemService(),传入全局 Context.VIBRATOR_SERVICE 的引用。这将返回一个类型为 Vibrator 的对象。您需要一个可用于整个扩展上下文的位置来存储此引用,所以将它放在一个位于 VibrationExtensionContext 内的新变量 vb 中。
vbc.vb = (Vibrator)a.getSystemService(Context.VIBRATOR_SERVICE);
7. 您现在应该打开 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。
3 个函数中的第二个: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。
3 个函数中的第三个: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];
12. 我们现在可以在这个 FREObject 上调用 getAsInt(); 来创建一个 int:
int duration = fro.getAsInt();
14. 最后,在我们的 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>
在
<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();
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 开发人员中心中的原生扩展示例。
评论