ANE for iOS

你這磨人的小婊子
記錄一下最近兩天的研究成果,直到寫出自己的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中創建一個靜態庫項目

Paste_Image.png

然后要導入Flash的一個頭文件叫FlashRuntimeExtensions.h
我的FlashRuntimeExtensions.h文件在這個目錄

Paste_Image.png

然后直接拖進去就好了,記得選拷貝

Paste_Image.png

接下來把.m里面的代碼都刪掉,(.h文件直接可以刪掉了)不用上了,然后導入Flash的頭文件
然后大概就是醬紫吧

Paste_Image.png

然后初始化這個逗比的擴展吧

這個是在FlashRuntimeExtensions.h文件中定義的初始化簽名(就是參數列表),必須按照這個參數列表去實現,方法名隨意

Paste_Image.png

這個就是初始化擴展

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啦

Paste_Image.png

接下來的事情就是編譯靜態庫做最后的打包工作啦

1.開始編譯,首先你需要選擇一個運行環境,模擬器運行呀還是真機模式,然后command+B,木有報錯就OK了

Paste_Image.png

如果編譯成功就會生成黃色框中的.a文件(紅色的名字說明還沒編譯或者沒有編譯成功)

編譯成功就是這樣的:


Paste_Image.png
小提示:
  • 選中.a文件然后右鍵 Show in Finder 就可以打開這個文件的目錄,媽媽再也不用擔心我找不到這個文件啦

2.打包ANE文件,把編譯好的.a文件然后在打包成.ane文件,然后在通過as3調用,打包我們使用一個好人開發的一個打包工具,能幫我們剩好多事情,閃亮登場 ??????????

Paste_Image.png

接下來配置一下,然后生成.ane文件,準備幾個文件

  • swc文件,這個文件是之前創建的as3庫項目生成的swc
  • .a文件,是剛才通過Xcode創建的編譯后的.a
  • .p12文件,可以自己通過FB創建一個證書

然后開始配置打包工具
swc路徑就是之前創建的as3庫項目生成的swc的路徑
java環境變量的這個路徑就是我現在填的
本機擴展配置里面的ID就是之前在提醒注意的那個ID一定要保持一致
版本隨便填嘍
本機擴展這個是Xcode生成的.a文件路徑
Initializer 這個里面填Xcode里面初始化擴展的方法名initHelloANE
如果是真機調試就選真機,模擬器就選模擬器
證書文件這個自己搞搞么好了,還有密碼
然后就是到處的ane文件路徑啦

填好后大概就是醬紫吧


Paste_Image.png

一切準備就緒后就摁下下面的按鈕吧,然后耐心的等待幾秒鐘,如果輸出如下信息則表示打包成功啦,趕快去測試一下ane吧。

Paste_Image.png

創建一個as3手機項目,然后導入打包好的ane文件,看到ane有一個錯誤,不影響使用效果


Paste_Image.png

然后就簡單啦,直接new一個之前as3庫里面的類然后直接調用方法就OK

var hello:HelloANE = new HelloANE();
hello.initChectHeadset();

我用的是真機調試,所以直接跑起來(布吉島為什么我在FB for Mac 打包手機項目直接閃退,然后又切換到Win平臺搞的Dome)

注意:有一個東西需要注意的在打包到手機里面的時候需要在構建打包里面把本機擴展里面ANE后面的那個 √ 打上,這下就木有錯誤啦

Paste_Image.png

接下來就開心的去調用啦,比如下面這樣:

Paste_Image.png

這個是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項目的是記得選構建環境(真機、虛擬機)

ANETool工具地址

啦啦啦,就醬紫吧......(如需人工幫助請按0)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,836評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,925評論 6 342
  • 黃堡文化研究 第315期作者:崔會婷編輯:秦隴華 初中畢業那年,在陳爐中學讀了一周高中,上學的感覺至今在我的心里都...
    primates閱讀 862評論 0 1
  • 我是一棵小草,我本來和我的朋友生活在一個美麗的花園里。 我們和人類融合的很好,他們給我們陽光,雨水,肥料,...
    帥氣的黃子陽閱讀 380評論 2 2
  • 淺層的思考,重復多遍,也不可能有任何進益,同一個層次的東西,練習得再多,輸出的始終只能是同樣的結果,無變化、無修正...
    Novalis閱讀 288評論 0 0