Adobe AIR教程:面向iOS設備的原生擴展
本文的主要內容如下
AIR Native Extension介紹
ANE的組成部分
ActionScript 3.0擴展
Objective-C 擴展
使用ADT打包ANE
使用ADT打包IPA
AIR Native Extension介紹
AIR Native Extension (ANE)是AIR 3.0的一項重要特性,簡單的說,它允許AIR應用程序通過擴展文件與原生應用程序類庫相互通訊,從而讓AIR應用實現一些只有原生程序才可以做到的功能。
在ANE出現以前,移動平臺上的AIR對系統的訪問非常有限,功能的實現都是封裝在封閉的,由Adobe定義好的ActionScript 3.0 API內,比如Accelerometer, GeoLocator等AS類。ANE則將AIR徹底開放出來,AIR不再針對具體的功能提供封閉的API,而是允許開發者通過AIR的擴展機制自由調用使用原生語言開發的類庫。這樣可以讓AIR應用程序享有與原生應用程序同等的機會,其意義對Flash技術來說是劃時代的。
ANE的組成部分
ANE支持向Windows、Mac OSX、Android和iOS各個平臺原生應用程序的擴展,本文只針對iOS平臺進行介紹。在iOS平臺中,ANE的組成部分基本分為ActionScript 3.0擴展類庫和Objective-C原生擴展類庫兩個部分,這兩個部分打包后生成AIR擴展文件(.ane),***和AIR應用程序一起打包成iOS原生應用IPA文件。如下圖所示。

圖1 ANE的組成部分
ActionScript 3.0擴展
ANE的AS擴展部分是一個SWC,AIR 3.0 SDK里為flash.external.ExtensionContext類添加了新的方法。如下例所示:
- import flash.external.ExtensionContext;
- ...
- private var ext:ExtensionContext;
- ...
- ext = ExtensionContext.createExtensionContext("com.adobe.appPurchase","");
在這個例子里,ExtensionContext通過靜態方法createExtensionContext()來獲得一個實例,參數com.adobe.appPurchase是這個擴展的ID,它非常重要,在擴展的配置文件里和應用程序描述文件中都需要用這個ID進行配對。
調用原生類中定義的方法可以用方法call()來實現,由于是同步調用,所以函數可以有返回值。如在原生類中定義的方法finish,可以用下面的代碼來調用。
- var result:Object = ext.call("finish");
我們還可以給ExtensionContext類添加事件偵聽,用來獲取從原生類中派發回來的事件。
- ext.addEventListener(StatusEvent.STATUS,onStatus);
- public function onStatus(e:StatusEvent):void{
- switch(e.code){
- case "removeTransaction":
- ...
- }
- }
Objective-C 擴展
接下來是原生類的部分,如果你注冊成為蘋果iOS開發者,那么你可以在蘋果開發者網站上免費下載Object-C的開發工具XCode。關于如何注冊成為蘋果iOS開發者,請參考我的這篇文章,如何成為一個合法的iOS開發者。
總的來說,Objective-C 雖然語法比較奇怪,但只要掌握了基本的規則,還是和ActionScript一樣易懂。OBJC擴展類需要引入一個FlashRuntimeExtension.h類包,它實現了和ActionScript溝通的接口。
引入FlashRuntimeExtension.h之后,可以用下面的代碼定義一個FREObject方法,FREObject是接口類型。這里要注意,與AS的接口包括函數返回值,都要定義成FREObject類型,比如代碼中的retVal。
- FREObject finishTransaction1(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
- NSLog(@"Finish Transaction Called");
- BOOL matchFound = NO;
- const uint8_t* str = nil;
- uint32_t len = -1;
- ......
- FREObject retVal;
- if(FRENewObjectFromBool(matchFound, &retVal) == FRE_OK){
- return retVal;
- }else{
- return nil;
- }
- }
要把FREObject方法定義成接口,還需要在ContextInitializer方法內進行配置,如下:
- //這里是需要定義的接口的數量
- *numFunctionsToTest = 6;
- //定義一個FRENamedFunction類型的實例func,初始化函數的個數
- FRENamedFunction* func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*6);
- //定義一個接口,name是字符串"getProducts",函數體是getProducts
- func[0].name = (const uint8_t*)"getProducts";
- func[0].functionData = NULL;
- func[0].function = &getProducts;
- func[1].name = (const uint8_t*)"startPayment";
- func[1].functionData = NULL;
- func[1].function = &startAppPayment;
- func[2].name = (const uint8_t*)"finish";
- func[2].functionData = NULL;
- func[2].function = &finishTransaction1;
- func[3].name = (const uint8_t*)"muted";
- func[3].functionData = NULL;
- func[3].function = &muted;
- func[4].name = (const uint8_t*)"restore";
- func[4].functionData = NULL;
- func[4].function = &restoreTrans;
- func[5].name = (const uint8_t*)"trans";
- func[5].functionData = NULL;
- func[5].function = &getTrans;
- *funcfunctionsToSet = func;
- ....
而ContextInitializer方法,是在原生擴展類的初始化函數ExtInitializer中指定的:
- void ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet,
- FREContextFinalizer* ctxFinalizerToSet) {
- NSLog(@"Extension Initialized");
- *extDataToSet = NULL;
- *ctxInitializerToSet = &ContextInitializer;
- *ctxFinalizerToSet = &ContextFinalizer;
- }
ExtInitializer是原生擴展的程序入口,它可以通過擴展配置文件extension.xml來定義:
- com.adobe.appPurchase
- libAppPurchase.a
- ExtInitializer
- ExtFinalizer
我介紹的這個順序,實際上就是實際程序編寫的思路,先確定接口,再實現連接。 也許有朋友和我一開始接觸OBJC的時候一樣,對這些代碼一頭霧水。沒有關系,在這篇教程里我只是對流程做簡短的介紹,具體的代碼解析會在本系列的***一篇教程里做更詳細的講解。那么接下來讓我來介紹下一個部分,打包擴展。
使用ADT打包ANE
在圖1中,我介紹了.ane文件的組成,它包括了AS類庫(.swc)和原生類(.a)兩個部分,以及剛才我們介紹的這個擴展配置文件extension.xml。那么要打包ane我們還需要哪些文件呢?

圖2 打包ANE所需要的文件
如圖2所示,所選擇的文件以及文件夾就是打包ANE所需要的所有文件,它包括:
1,AIR SDK打包應用程序和類庫(bin,lib)
2,ActionScript擴展類包.swc,如圖ANE_IAP_ASLib.swc
3,ActionScript擴展類包.swf,如圖library.swf,可以通過將SWC的文件擴展名改成ZIP后解壓縮得到。
4,Objective-C擴展類包.a,如圖libAppPurchase.a,可以通過在Xcode中編譯項目得到。
5,擴展配置文件XML,如圖extension.xml
6,一個打包證書,如圖selfsigned.p12,可以通過Flash CS5的AIR發布設置生成。
一切就緒后便可以使用命令行進行打包,注意路徑,下例路徑為當前文件夾。
- bin/adt -package -storetype pkcs12 -keystore selfsigned.p12 -storepass 1234 -target ane ext/InApp.ane extension.xml -swc ANE_IAP_ASLib.swc -platform iPhone-ARM library.swf libAppPurchase.a
使用ADT打包IPA
.ane文件打包成功后,便可以用來打包IPA文件,也就是iOS應用程序包。如果你對開發iOS應用的必要流程還不很清楚,請參閱我的這篇教程,如何使用iOS開發者授權以及如何申請證書。我以前介紹過如何用Flash Professional CS5打包IPA,今天主要介紹如何用AIR SDK的打包工具ADT來生成含有ANE擴展的IPA。

圖3 使用ADT生成含有ANE擴展的IPA所需要的文件
如圖3所示,所選擇的文件就是生成IPA的必要文件:
1,應用程序文件SWF,如圖是ANE_IAP_Example.swf。
2,開發者設備授權文件.mobileprovision,如圖是ghostbride_dev.mobileprovision。
3,開發者簽名證書文件.p12,如圖是jameslidevelopment.p12。
4,應用程序描述文件XML,如圖是info-app.xml。
5,擴展包路徑,如圖是ext
6,如果應用程序有圖標圖片,還需要圖標文件夾,如圖是icon
在應用描述文件XML中,需要對擴展追加一個定義:
- com.adobe.appPurchase
這里可以看到,在AS擴展類、擴展配置文件extension.xml和應用描述文件info-app.xml中都指定了一個統一擴展的ID: com.adobe.appPurchase。
利用下面的命令行可以打包生成Main.ipa:
- bin/adt -package -target ipa-test-interpreter -provisioning-profile ghostbride_dev.mobileprovision -storetype pkcs12 -keystore jameslidevelopment.p12 -storepass 1234 Main.ipa info-app.xml ANE_IAP_Example.swf -extdir ext icon