前回は、Adobe AIR 3で追加された新機能「ネイティブ拡張(Native Extensions)」の概要について説明しました。今回から2回にわたり、ネイティブ拡張の開発方法について解説します。

必要なもの

Eclipse

XCode

ネイティブ拡張を開発する手順

ネイティブ拡張を開発するには、下記のような手順で行います。

  1. ネイティブ拡張ライブラリの作成
    プラットフォームに依存したネイティブコードを使用して作成します。
  2. ネイティブ拡張ActionScriptライブラリの作成
    ネイティブ拡張ライブラリと連携するためのActionScriptライブラリを作成します。
  3. ネイティブ拡張記述ファイルの作成
    ネイティブ拡張ライブラリについて定義したXMLファイルを作成します。
  4. ANEファイルへパッケージング
    上記3つの要素をパッケージングして、ANEファイルを作成します。

前編では「1. ネイティブ拡張ライブラリの作成」について、後編では2~4、そして作成したANEファイルをAIRアプリへ組み込む方法についても解説します。

ここでは、AndroidやiOSを対象として、「HelloWorld」を出力するネイティブ拡張を作成し、それを使ってFlexモバイルアプリを作成します。

ネイティブ拡張ライブラリの作成

AIR 3には、ネイティブ拡張ライブラリを開発する上で役立つネイティブAPIが用意されています。 ネイティブAPIは、2つの言語で利用することができます。

  • ネイティブJava API :Java言語
  • ネイティブC API : C言語

また、プラットフォームによって対応しているネイティブAPIが異なります。下表を参照してください。

各プラットフォームで使えるネイティブAPI
プラットフォーム ネイティブAPI ネイティブ拡張開発言語 ネイティブ拡張ライブラリ
Android C API/Java API C言語/Java Shared library(.so) Java Archive library(.jar)
iOS C API C言語/Objective-C Static library(.a)
Windows C API C言語/C++/C# Dynamic Link Library(.dll)
Mac OS X C API C言語/Objective-C Framework(.framework)

それでは各ネイティブAPIを使って、「HelloWorld文字列を返す」というネイティブ拡張ライブラリを作成してみましょう。

ネイティブJava APIを使ったネイティブ拡張ライブラリの作成

ネイティブJava APIを使ってネイティブ拡張ライブラリを開発するには、AIR 3SDK内にある下記のJARファイルが必要です。

AIR 3 HOME/lib/android/FlashRuntimeExtensions.jar

JARファイルには、ネイティブ拡張するためのクラスやインターフェイスが定義されています。 詳細については、リファレンス(英語)を参照してください。

Javaプロジェクトの作成

今回は、ネイティブ拡張ライブラリを作成するためにEclipseを使用します。まずは、メニューの[ファイル]→[新規]→[Javaプロジェクト]から、Javaプロジェクトを作成します。

[新規Javaプロジェクト]ウィザードが開くので、プロジェクト名を「HelloWorldANE」として[終了]ボタンをクリックしてプロジェクトを作成します。

次に、前述したネイティブJava APIのFlashRuntimeExtensions.jarをビルドパスに設定します。

これで開発環境が整いました。

ネイティブ拡張関数クラスの作成

まず、com.adobe.fre.FREFunctionを実装したネイティブ拡張関数クラスを作成します。ネイティブ拡張関数とは、ActionScript側からネイティブ拡張コンテキスト(次の項目を参照)経由で呼び出すことができる関数のことです。実装する関数は、下表の通りです。このクラスの役割は、ネイティブコードを実装することです。そして、ネイティブ拡張コンテキストから呼び出されます。

ネイティブ拡張関数クラスで実装する関数
関数名 目的
call この関数の中にロジックを記述します。ActionScirptにデータを返したい場合は、FREObjectに変換して返り値にします。

サンプルでは、ActionScriptに文字列「Hello World」を返すために、FREObject.newObjectを使ってFREObjectインスタンスを生成しています。FREObjectについてはリファレンスを参照してください。

ソースコード:Java/HelloWorldANE/src/example/ane/android/functions/GatHelloWorldFunction.java

public class GatHelloWorldFunction implements FREFunction { public GatHelloWorldFunction() { } public FREObject call(FREContext arg0, FREObject arg1[]) { try { return FREObject.newObject("Hello World"); } catch (FREWrongThreadException e) { e.printStackTrace(); return null; } } }

ネイティブ拡張コンテキストクラスの作成

次に、com.adobe.fre.FREContextクラスを継承してネイティブ拡張コンテキストクラスを作成します。実装する関数は、下表の通りです。このクラスの役割は、ネイティブ拡張関数クラスを管理することです。

ネイティブ拡張コンテキストクラスで実装する関数
関数名 目的
getFunctions ネイティブ拡張コンテキストに登録するネイティブ拡張関数マップを返します。このマップは、キーを関数名、値をFREFunctionのインスタンスとします。このマップが、ネイティブ拡張コンテキスト内部で登録されます。
dispose ネイティブ拡張コンテキストが破棄されるときに呼びだされる関数。

サンプルでは、関数名GatHelloWorldとGatHelloWorldFunctionネイティブ拡張関数クラスのインスタンスを関連付けています。よって、ActionScriptからGatHelloWorldをキーにしてGatHelloWorldFunctionネイティブ拡張関数を指定することができます。

ソースコード:Java/HelloWorldANE/src/example/ane/android/HelloWorldContext.java

public class HelloWorldContext extends FREContext { public HelloWorldContext() { } public Map<String, FREFunction> getFunctions() { HashMap<String, FREFunction> result = new HashMap<String, FREFunction>(); //関数の定義 result.put("GatHelloWorld", new GatHelloWorldFunction()); return result; } public void dispose() { //コンテキストの破棄処理 } }

ネイティブ拡張クラスの作成

次に、com.adobe.fre.FREExtensionインターフェイスを実装したネイティブ拡張クラスを作成する必要があります。実装する関数は、下表の通りです。このクラスの役割は、com.adobe.fre.FREContextを生成することです。なお、このクラスはネイティブ拡張ごとに1つ作成します。

ネイティブ拡張クラスで実装する関数
関数名 目的
initialize ネイティブ拡張が初期化されるときに呼びだされる関数。ActionScriptのExtensionContext.createExtensionContext()を呼び出すことで実行されます。
createContext ネイティブ拡張コンテキストを生成するための関数。引数の値を用いて目的に応じたコンテキストを作成することも可能。
dispose ネイティブ拡張が破棄されるときに呼びだされる関数。ActionScriptのExtensionContext.dispose()を呼び出すことで実行されます。

サンプルでは、createContextをHelloWorldContextクラスのインスタンスが返るように実装しています。

ソースコード:Java/HelloWorldANE/src/example/ane/android/HelloWorldExtension.java

public class HelloWorldExtension implements FREExtension { public HelloWorldExtension() { } public FREContext createContext(String arg) { //コンテキスト作成 FREContext context = new HelloWorldContext(); return context; } public void initialize() { //ネイティブ拡張初期化処理 } public void dispose() { //ネイティブ拡張破棄処理 } }

図5は、これまでの作業後のJavaProjectのファイル構成です。

JARファイルの作成

最後にJava Projectをコンパイルします。そしてコンパイルエラーがなければ、右クリックしてエクスポートからJARファイルを作成します。

ネイティブC APIを使ったネイティブ拡張ライブラリの作成

ネイティブC APIを使ってネイティブ拡張ライブラリを開発するには、AIR 3SDK内にある下記のヘッダーファイルが必要です。

AIR 3 HOME/include/FlashRuntimeExtensions.h

また、AndroidとWindowsでコンパイルする場合、下記のライブラリファイルが必要になります。

ライブラリファイルが必要なプラットフォーム
プラットフォーム ライブラリ
Android AIR 3 HOME/lib/android/FlashRuntimeExtensions.so
Windows AIR 3 HOME/lib/win/FlashRuntimeExtensions.lib

ヘッダーファイルには、AIRとネイティブコード間のインターフェイスと型と関数が定義されています。詳細については、リファレンスを参照してください。

具体的な作成方法の解説に入りますが、ここでは開発言語としてC++を使用しています。

Cocoa Touch Static Libraryプロジェクトの作成

今回は、ネイティブ拡張ライブラリを作成するためにXCodeを使用します。まずは、メニューの[File]→[New]→[New Project]から、Cocoa Touch Static Libraryプロジェクトを作成します。

[Choose a template for your new project]ウィザードが開くので、[Cocoa Touch Static Library]を選択して[Next]ボタンをクリックします。

次の画面では、[Product Name]に「HelloWorldANE」を入力して、[Company Identifier]に「example.ane」と入力して[Next]ボタンをクリックします。

Cocoa Touch Static Libraryプロジェクトを作成したら、前述したネイティブC APIのFlashRuntimeExtensions.hをビルドパスに設定します。FlashRuntimeExtensions.hを追加するには、プロジェクトのコンテキストメニュー→[Add Files to]からFlashRuntimeExtensions.hを選びます。

これで開発環境が整いました。

ネイティブ拡張関数の作成

ネイティブ拡張関数は、ヘッダーファイルに定義されているFREFunction関数型で実装する必要があります。ネイティブ拡張関数とは、ActionScript側からネイティブ拡張コンテキスト(次の項目を参照)経由で呼び出すことができる関数のことです。FREFunction関数型は、下表の通りです。このネイティブ拡張関数は、ネイティブ拡張コンテキストに登録して、ActionScriptから呼び出されます。

ネイティブ拡張関数の関数型
関数型 目的
FREFunction この関数型の引数と同じ関数を作成して、その中にロジックを記述します。ActionScirptにデータを返したい場合は、FREObjectに変換して返り値にします。引数は下記の通りです。
【argc】
ネイティブ拡張関数の引数の数。
【argv】
ネイティブ拡張関数の引数の配列。

サンプルでは、ActionScriptに文字列「HelloWorld」を返すために、FREObject.newObjectを使ってFREObjectインスタンスを生成しています。FREObjectについてはリファレンスを参照してください。

ソースコード:iOS/HelloWorldANE/HelloWorldANE.h

FREObject GetHelloWorld( FREContext ctx, void* funcData, uint32_t argc, FREObject arg[] ); ソースコード:iOS/HelloWorldANE/HelloWorldANE.m FREObject GetHelloWorld(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) { const char *str = "HelloWorld"; FREObject retStr; FRENewObjectFromUTF8(strlen(str)+1, (const uint8_t *)str, &retStr); return retStr; }

ネイティブ拡張コンテキストの初期化関数と破棄関数の作成

ネイティブ拡張コンテキストの初期化関数と破棄関数は、ネイティブ拡張の初期化時に必要であり、ヘッダーファイルに定義されている関数型で実装しなければなりません。実装する関数型は、下表の通りです。この初期化関数を使用して、ネイティブ拡張コンテキストに登録するネイティブ拡張関数を指定することができます。

ネイティブ拡張コンテキストの初期化関数型と破棄関数型
関数型 目的
FREContextInitializer

ネイティブ拡張コンテキストが初期化されるときに呼びだされる関数。引数は下記の通りです。
【numFunctionsToTest】
ネイティブ拡張コンテキストに登録するネイティブ拡張関数の数を指定します。
【functionsToSet】
ネイティブ拡張コンテキストに登録するネイティブ拡張関数配列を返します。この配列は、型がFRENamedFunctionで、長さがnumFunctionsToTestになります。また、ネイティブ拡張コンテキスト内部で登録されます。

FRENamedFunction型は、ネイティブ拡張関数を登録する際に使用します。引数は下記の通りです。
【name】
ネイティブ拡張関数名を指定します。
【functionData】
ネイティブ関数に渡すデータのポインタを指定します。
【function】
ネイティブ関数のポインタを指定します。

FREContextFinalizer ネイティブ拡張コンテキストが破棄されるときに呼びだされる関数。

サンプルでは、関数名GatHelloWorldとGetHelloWorldネイティブ拡張関数を関連付けています。よって、ActionScriptからGatHelloWorldをキーにしてGatHelloWorldネイティブ拡張関数を指定することができます。

ソースコード:iOS/HelloWorldANE/HelloWorldANE.h

void ContextInitializer( void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet ); void ContextFinalizer(FREContext ctx); ソースコード:iOS/HelloWorldANE/HelloWorldANE.m void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet) { *numFunctionsToTest = 1; FRENamedFunction* func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*1); func[0].name = (const uint8_t*)"GetHelloWorld"; //ネイティブ拡張関数名 func[0].functionData = NULL; func[0].function = &GetHelloWorld; //ネイティブコード関数のポインタ *functionsToSet = func; } void ContextFinalizer(FREContext ctx) { return; }

ネイティブ拡張の初期化関数と破棄関数の作成

ネイティブ拡張では、初期化関数と破棄関数をネイティブ拡張ライブラリに定義する必要があり、ヘッダーファイルに定義されている関数型で実装しなければなりません。実装する関数型は、下表の通りです。また、この2つの関数はネイティブ拡張ごとに1組作成します。この初期化関数を使用して、ネイティブ拡張コンテキストの初期化関数と破棄関数を登録することができます。

ネイティブ拡張の初期化関数型と破棄関数型
関数型 目的
FREInitializer ネイティブ拡張が初期化されるときに呼びだされる関数。ActionScriptのExtensionContext.createExtensionContext()を呼び出すことで実行されます。引数は下記の通りです。
【ctxInitializerToSet】
ネイティブ拡張コンテキスト初期化関数のポインタを指定します。
【ctxFinalizerToSet】
ネイティブ拡張コンテキスト破棄関数のポインタを指定します。
FREFinalizer ネイティブ拡張が破棄されるときに呼びだされる関数。ActionScriptのExtensionContext.dispose()を呼び出すことで実行されます。

サンプルでは、ContextInitializer関数をネイティブ拡張コンテキストの初期化関数に指定し、ContextFinalizer関数をネイティブ拡張コンテキストの破棄関数に指定しています。

ソースコード:iOS/HelloWorldANE/HelloWorldANE.h

void ExtInitializer( void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet ); void ExtFinalizer(void* extData); ソースコード:iOS/HelloWorldANE/HelloWorldANE.m void ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet) { *extDataToSet = NULL; *ctxInitializerToSet = &ContextInitializer; *ctxFinalizerToSet = &ContextFinalizer; } void ExtFinalizer(void* extData) { return; }

図13は、これまでの作業後のCocoa Touch Static Libraryプロジェクトのファイル構成です。

aファイルの作成

Cocoa Touch Static Library Projectのビルドが通ると図14のようになります。

今回のサンプルでは、libHelloWorldANE.aが生成されます。このファイルをコピーしたい場合は、libHelloWorldANE.aのコンテキストメニューの[Show in Finder]をクリックします。

「1. ネイティブ拡張ライブラリの作成」の解説は以上です。後編では、「2. ネイティブ拡張ActionScriptライブラリの作成」「3. ネイティブ拡張記述ファイルの作成」「4. ANEファイルへパッケージング」、そして作成したANEファイルをAIRアプリへ組み込む方法を解説します。

関連情報

ネイティブ拡張(Native Extensions)入門

Flash Builder 4.6 でネイティブ拡張(Native Extensions)を使ってみよう