最近由于项目需要,用到了些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,因此这里要实现两个接口,代码见下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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中模板帮我们定义好了两个宏,这样写代码的时候会方便很多:
1
2
|
#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个接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//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给放进来啦,具体步骤我就不详诉了,请参考微信官方文档。上代码啦:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
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强大……