你這磨人的小婊子
記錄一下最近兩天的研究成果,直到寫出自己的ANE,網上關于ANE的列子也不少,不知道別人怎么樣,我是看的有點云里霧里的,最后參考了好多資料才逐步寫出了自己的ANE。
第一步:
首先創建一個as庫項目
里面創建ExtensionContext
對象,溝通全靠它了,如果想調用某個方法就call("functionName")
一下就好了
開始創建ExtensionContext
對象,ExtensionContext.createExtensionContext(ID,null);
會返回一個ExtensionContext
對象,如果返回值等于null的時候檢查一下ID是否一致,這個ID標識符的值與擴展描述符文件中的 id 元素的值相同(后面會用工具幫助我們生成描述文件,但是這個ID必須對應上)
as3 庫項目部分 定義了一個簡單的功能,檢測耳機插入initChectHeadset
public class HelloANE extends EventDispatcher
{
private var ec:ExtensionContext = null;
public static const ID:String = "com.xd.hello";
public function HelloANE(target:IEventDispatcher=null)
{
super(target);
//創建一個ExtensionContext實例,擴展ID需要注意一下,打包的時候會用到
ec = ExtensionContext.createExtensionContext(ID,null);
if(ec){
//添加事件用來監聽里面拋出的事件,或者消息用的
ec.addEventListener(StatusEvent.STATUS,onStatus);
}
}
/**
* 初始化耳機檢測
*/
public function initChectHeadset():void
{
ec.call("initChectHeadset");
}
/**
* 耳機檢測事件回調,返回耳機狀態
*/
protected function onStatus(event:StatusEvent):void
{
trace("事件回調 \n"+JSON.stringify(event));
}
}
第二步
在Xcode中創建一個靜態庫項目
然后要導入Flash的一個頭文件叫FlashRuntimeExtensions.h
我的FlashRuntimeExtensions.h
文件在這個目錄
然后直接拖進去就好了,記得選拷貝
接下來把.m
里面的代碼都刪掉,(.h
文件直接可以刪掉了)不用上了,然后導入Flash的頭文件
然后大概就是醬紫吧
然后初始化這個逗比的擴展吧
這個是在FlashRuntimeExtensions.h
文件中定義的初始化簽名(就是參數列表),必須按照這個參數列表去實現,方法名隨意
這個就是初始化擴展
void initHelloANE(
void** extDataToSet ,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet
)
{
}
這個有一個需要注意的地方,就是要調用一個方法的話必須把方法寫在調用者之前,不然不能訪問,或者提前在上面聲明一下
然后初始化一下as要調用的方法簽名(就是在as3里面要調用那些方法首先你要在這個地方注冊一下)
當然它也有一個規范的,方法簽名要一樣
typedef void (*FREContextInitializer)(
void* extData ,
const uint8_t* ctxType ,
FREContext ctx ,
uint32_t* numFunctionsToSet,
const FRENamedFunction** functionsToSet
);
接下來我們去實現這個方法
//
// as調用的方法簽名初始化
//
void contextInit(
void* extData ,
const uint8_t* ctxType ,
FREContext ctx ,
uint32_t* numFunctionsToSet,
const FRENamedFunction** functionsToSet
)
{
// 需要注冊的方法的數量
*numFunctionsToSet = 1;
FRENamedFunction *functions = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)* *numFunctionsToSet);
//這邊的name就是外面要調用的方法名,要對應的上
functions[0].name = (const uint8_t *)"initChectHeadset";
functions[0].functionData = NULL;
//這個是ANE里面需要執行的方法名(有點類似于一個映射,調用外面的方法,然后映射到里面具體的方法)
functions[0].function = &initChectHeadset;
//然后將方法列表設置一下
*functionsToSet = functions;
}
在initChectHeadset
方法里面我們就具體去實現我們的功能了,然而它也是規范的,方法簽名,可以點進去看他的定義,仍然需要一堆的參數的,然后繼續實現它
typedef FREObject (*FREFunction)(
FREContext ctx,
void* functionData,
uint32_t argc,
FREObject argv[]
);
initChectHeadset
的實現,需要注意的一點FREContext
這個東東需要保存一下,后面需要他回傳一些參數到調用者的,我們需要在聲明一個全局的FREContext
對象來保存它,FREObject
對象是我們返回出去和傳遞進來的一個通用對象,而且只能是它
Audio的東西所以需要導入一下頭文件#import <AudioToolbox/AudioToolbox.h>
/**
初始化耳機檢測
*/
FREObject initChectHeadset(
FREContext ctx,
void* functionData,
uint32_t argc,
FREObject argv[]
)
{
//保存FREContext對象
eventContext = ctx;
//下面是耳機的一些檢測方法,最后有一個回調audioRouteChangeListenerCallback
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionSetActive(YES);
AudioSessionAddPropertyListener(
kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
nil);
return NULL;
}
實現audioRouteChangeListenerCallback
回調方法和上面一樣,點進去看看頭文件里面是怎么定義的
//
// kAudioSessionProperty_AudioRouteChange 事件回調
//
void audioRouteChangeListenerCallback(
void * inClientData,
AudioSessionPropertyID inID,
UInt32 inDataSize,
const void * inData)
{
if (inID != kAudioSessionProperty_AudioRouteChange) {
return;
}
CFDictionaryRef routeChangeDic = (CFDictionaryRef)inData;
CFNumberRef routeChangeReasonRef = CFDictionaryGetValue(routeChangeDic, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
SInt32 routeChangeReason;
CFNumberGetValue(routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
NSLog(@"沒有耳機");
dispatchMsgEvent(@"yes");
}else if(routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable){
NSLog(@"有耳機");
dispatchMsgEvent(@"no");
}
}
接下來是怎么把數據在傳遞給方法的調用者,也就是傳遞到as3里面去,可以通過FREDispatchStatusEventAsync
這個方法,他可以給as3中發送一個事件,在外面可以監聽到
在調用這個方法之前先去看看它的定義,如下:
FREResult FREDispatchStatusEventAsync(
FREContext ctx ,
const uint8_t* code ,
const uint8_t* level
);
看見這個方法是需要一個FREContext
對象作為參數,我們之前保存的FREContext
可以排上用場了,剩下的兩個參數是as3中StatusEvent
對象的兩個屬性,可以根據這兩個屬性做一些判斷啥的
在看看dispatchMsgEvent
方法的實現吧,在這個里面發送對外的事件,也就是as3能監聽到的事件
void dispatchMsgEvent(NSString *msg)
{
if (eventContext == NULL) {
return;
}
NSString *eventName;
if ([msg isEqualToString:@"yes"] || [msg isEqualToString:@"no"]) {
eventName = @"audioRouteChange";
}else{
eventName = @"ordinaryType";
}
const uint8_t *eventCode = (const uint8_t *)[eventName UTF8String];
const uint8_t *msgCode = (const uint8_t *)[msg UTF8String];
//把事件派發出去,我們在一開始的時候已經添加了??
FREDispatchStatusEventAsync(eventContext, eventCode, msgCode);
}
Product->Scheme->Edit Scheme...
然后設置一下項目的編譯配置設置為Release就OK啦
接下來的事情就是編譯靜態庫做最后的打包工作啦
1.開始編譯,首先你需要選擇一個運行環境,模擬器運行呀還是真機模式,然后command+B,木有報錯就OK了
如果編譯成功就會生成黃色框中的.a
文件(紅色的名字說明還沒編譯或者沒有編譯成功)
編譯成功就是這樣的:
小提示:
- 選中.a文件然后右鍵 Show in Finder 就可以打開這個文件的目錄,媽媽再也不用擔心我找不到這個文件啦
2.打包ANE文件,把編譯好的.a
文件然后在打包成.ane
文件,然后在通過as3調用,打包我們使用一個好人開發的一個打包工具,能幫我們剩好多事情,閃亮登場 ??????????
接下來配置一下,然后生成.ane
文件,準備幾個文件
- swc文件,這個文件是之前創建的as3庫項目生成的swc
- .a文件,是剛才通過Xcode創建的編譯后的.a
- .p12文件,可以自己通過FB創建一個證書
然后開始配置打包工具
swc路徑就是之前創建的as3庫項目生成的swc的路徑
java環境變量的這個路徑就是我現在填的
本機擴展配置里面的ID就是之前在提醒注意的那個ID一定要保持一致
版本隨便填嘍
本機擴展這個是Xcode生成的.a文件路徑
Initializer 這個里面填Xcode里面初始化擴展的方法名initHelloANE
如果是真機調試就選真機,模擬器就選模擬器
證書文件這個自己搞搞么好了,還有密碼
然后就是到處的ane文件路徑啦
填好后大概就是醬紫吧
一切準備就緒后就摁下下面的按鈕吧,然后耐心的等待幾秒鐘,如果輸出如下信息則表示打包成功啦,趕快去測試一下ane吧。
創建一個as3手機項目,然后導入打包好的ane文件,看到ane有一個錯誤,不影響使用效果
然后就簡單啦,直接new一個之前as3庫里面的類然后直接調用方法就OK
var hello:HelloANE = new HelloANE();
hello.initChectHeadset();
我用的是真機調試,所以直接跑起來(布吉島為什么我在FB for Mac 打包手機項目直接閃退,然后又切換到Win平臺搞的Dome)
注意:有一個東西需要注意的在打包到手機里面的時候需要在構建打包里面把本機擴展里面ANE后面的那個 √ 打上,這下就木有錯誤啦
接下來就開心的去調用啦,比如下面這樣:
這個是StatusEvent
事件對象, 里面的level和code也都在里面,后面客官請隨意??????????
{
"level": "yes",
"target": {
"actionScriptData": null
},
"currentTarget": {
"actionScriptData": null
},
"bubbles": false,
"eventPhase": 2,
"cancelable": false,
"type": "status",
"code": "audioRouteChange"
}
需要注意的地方:
- ANE中的類型和as3是有差異的,所以類型轉換的地方需要注意一下
- 在使用工具打包的時候如果發生錯誤檢查一下ID是否一致,路徑是否正確,初始化方法名稱是否正確
- 在初始化簽名方法的時候上面的
numFunctionsToSet
參數是方法的數量 - 打包的時候記得勾選本地擴展包
- 構建Xcode項目的是記得選構建環境(真機、虛擬機)