開發Adobe AIR的原生擴展
目錄:
需求
預備知識
對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替換為一個全局范圍的變量,使用一個控件來設置此變量?,F在,編寫應用程序的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。