IOS ANE的坑爹之路(一)

最近由于项目需要,用到了些ANE(Air Native Extension)的东西,让Flash可以在iOS调用一些东西,实在太坑爹了,让我忍不住拿出来分享一下 (天呀,我同时再弄js、php、c++、objective-c、flash的东西,崩溃呀)。

ANE,顾名思义,也就是Flash Air的本地扩展。Air虽然可以实现跨平台,但是当需要调用一些平台特性的时候,必然还是会遇到瓶颈的,例如在iOS上无法直接调用音频、图像、支付等接口,这时候就需要ANE出场了。iOS平台的ANE可以用c/c++/oc开发,向Air端提供相应的接口。

现在很多iOS本身的特性都有官方的ANE支持了,但是很多时候开发者还是会需要一些特定的ANE,例如什么摄像、录音、文件处理等,本地接口的效率还是会比Air要高出很多的。本文就以Air调用现在iOS上非常火的微信的API为例,说说ANE的坑爹之路吧。

第一步:定义好ActionScript的接口

首先,打开你的flash builder,创建一个Flex库项目,开始定义你的ActionScript接口吧。我们调用微信API要干嘛呢?当然是发消息给朋友啦,不过发消息前要向微信注册我们的应用ID,因此这里要实现两个接口,代码见下:

package com.rolfzhang.ane
{
  import flash.events.Event;
  import flash.external.ExtensionContext;

  public class WeixinApi
  {
    private static var extContext:ExtensionContext = null;
    public static var isRegistered:Boolean = false;

    public static var WXSceneSession:int = 0;
    public static var WXSceneTimeline:int = 1;

	//构造函数
    public function WeixinApi(appIdStr:String){
      initExtension();      
      this.registerApp(appIdStr);
    }

    private static function initExtension():void{
      if (!extContext) {
        //这里通过一个自定义的id去获取相应的Extension,
        //必须和objc端的配置文件一致
        extContext = ExtensionContext.createExtensionContext("com.rolfzhang.ane.Weixin", null);
      }
    }

    private function registerApp(appIdStr:String):Boolean {
      if(extContext && !isRegistered){
        //注册微信API,所有Extension方法都是通过call来调用
        isRegistered = extContext.call("initWXApi",appIdStr);
      }
    }

    public function sendTextMessage(msg:String, scene:int):Boolean {
      if(!isRegistered) return false;
      //发送消息啦,scene为0表示发送给朋友,1发到朋友圈
      return extContext.call("sendTextMessage",msg,scene) as Boolean;
    }
  }
}

这一步不是很复杂,extContext调用的接口也就是objc要实现的方法,先定义接口在开发是比较好的实践。

把这段代码编译好,生成swc文件,后面会用到。

第二步:用objc去调用微信API

这一步网上说的一般都比较复杂,除了写objc,还要解压swc文件,又要用命令行打包神马的,烦死了……咱们还是用些先进点的工具来解决好了。我在Github上找到了一个ANE的XCode模板,把很多东西都做好了,我们只需要老老实实的写objc代码,而不用管那些繁琐的打包工作(安装方法请自行阅读README.md)。

接下来,创建XCode的ANE项目:

红线部分必须和ActionScript接口里面定义的是一样的 ,当然这个也可以在extension.xml配置文件里面修改。

然后我们需要到Air SDK的文件夹里面找一个FlashRuntimeExtensions.h文件,放入我们的项目中,这个是Adobe提供的iOS与Air交互的接口。

创建好的项目中主有以下一些文件:

  • objc代码:Weixin.h、Weixin.m
  • 配置文件:extension.xml、platformoptions.xml
  • 打包脚本:generateANE.sh

Weixin.h中模板帮我们定义好了两个宏,这样写代码的时候会方便很多:

#define ANE_FUNCTION(f) FREObject (f)(FREContext ctx, void *data, uint32_t argc, FREObject argv[])
#define MAP_FUNCTION(f, data) { (const uint8_t*)(#f), (data), &(f) }

并且已经定义好了4个接口:

//as端首次调用createExtensionContext时进行初始化
//必须和extension.xml里面配置一致
void WeixinExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet);

//extension被最终释放时调用,做一些clean up的工作
void WeixinExtFinalizer(void* extData);

//ExtensionContext初始化,
//接口配置,获取全局context,对象初始化
void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet);

//context被释放,做一些clean up的工作
void ContextFinalizer(FREContext ctx);

//我们要实现的方法
ANE_FUNCTION(initWXApi);
ANE_FUNCTION(sendTextMessage);

我们接下来的工作主要就是写实现各个ANE_FUNCTION,然后在ContextInitializer里面配置上去。在此之前,当然还要把微信的SDK给放进来啦,具体步骤我就不详诉了,请参考微信官方文档。上代码啦:

void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, 
        uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{
  static FRENamedFunction func[] = 
  {	//这里配置as能调用的方法
    MAP_FUNCTION(initWXApi, NULL),
    MAP_FUNCTION(sendTextMessage, NULL)
  };

  *numFunctionsToTest = sizeof(func) / sizeof(FRENamedFunction);
  *functionsToSet = func;
}

ANE_FUNCTION(initWXApi)
{
  //argv是as端传过来的参数,
  //类型是FREObject,需要一些转换才能被调用
  NSString * apiId = getStringFromFREObject(argv[0]);
  //向微信注册AppID
  BOOL success = [WXApi registerApp:appId];
  //返回参数需要是FREObject类型
  return createFREBool(success);
}

ANE_FUNCTION(sendTextMessage)
{
  //获取参数
  NSString * msg = getStringFromFREObject(argv[0]);
  int32_t scene;
  FREGetObjectAsInt32(argv[1], &scene);
  //发消息
  SendMessageToWXReq* req = [[[SendMessageToWXReq alloc] init]autorelease];
  req.bText = YES;
  req.text = msg;
  req.scene = scene;
  BOOL success = [WXApi sendReq:req];
  return createFREBool(success);
}

//将FREObject转成NSString
NSString * getStringFromFREObject(FREObject obj)
{
  uint32_t length;
  const uint8_t *value;
  FREGetObjectAsUTF8(obj, &length, &value);
  return [NSString stringWithUTF8String:(const char *)value];
}
//将BOOL转成FREObject
FREObject createFREBool(BOOL value)
{
  FREObject fo;
  FRENewObjectFromBool(value, &fo);
  return fo;
}

OK,写好代码只好当然是编译,然后ANE打包啦。什么,你要用终端写命令行?有了XCode的模板就不用这么麻烦了,选择Weixin.ane那个target,Command+B~ 一个微信的ANE就被build出来了。

第三步:调用ANE

这里就只是简单的测试ANE啦,不过比较纠结的是,目前无法在桌面端的模拟器里面进行测试,必须要打包成ipa传到iOS设备中运行。目前Flash Builder无法像XCode那样断点调试,而且代码分布在三个地方(as接口/objc/项目代码),真是错在哪都不知道,写错个字母也够你找几个小时了,很多的时间都浪费在些小问题上了。据说flash builder 4.7会支持联机调试,那样应该就会好很多。

ANE的调用很简单啦,创建一个Flex手机项目,选择iOS平台,然后在“项目属性>构建路径>本机扩展”里面添加ANE文件(XCode生成的ANE,XCode里面 右键>show in Finder 就找到了)。最后,还要检查下“构建打包”里面,这个ANE后面有没打上勾,没打上就勾上去。然后就import那个as库,调用相应的代码就行了。

iOS应用打包还需要提供证书和配置文件,怎么取得这个就自行研究吧。

 

以上的代码只是示例而已,源码不便提供,有什么问题请留言。接下来还会写一下ANE的应用回调、事件处理及一些经验和注意事项,敬请期待。

 

========

老实说这种混合体还是挺恶心的,Air基本只能做界面和交互功能,感觉还没有js+phonegap强大……

发布者

Rolf

伪文艺IT攻城师,热爱前端,热爱互联。

《IOS ANE的坑爹之路(一)》有25个想法

  1. 你好~我想找你帮个忙开发一些微信的功能!希望可以合作。
    如果方便请联系我详谈。价格与功能我们详谈。。
    QQ:201375976
    微信:openstar520
    email:201375976@qq.com
    电话:13532222849

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

[喜欢] [嘻嘻] [奋斗] [问号] [鼓掌] [泪] [酷] [强] [耶] [握手] [心] [给力] [神马] [围观] [奥特曼] more »