‘Flex’ カテゴリーのアーカイブ

AIR for iOSのSWFロード

2012 年 3月 31 日 土曜日 kosuke

漢字の練習帳が全てリリースされ一段落ついたので、次回更新にむけ改善したい箇所を検証しつつ対応しよう。

まず最初に漢字の練習画面で画面に表示されるまでにかかる時間を短縮したい。
ここで時間がかかるのは、漢字を構成する一画一画の線の画像ファイルをロードしているから。
さらに直列にロードしているので、画数の多い漢字ほど時間がかかっている。

並列にロードすればある程度時間は減るのだが、これだと同時にロードする画像ファイルが多くなると、ビジーインジケーターが回転しないままロード完了までフリーズしたかのような挙動になってしまう。そうなると回避の為に数ファイル毎に分け間を入れることになり結局問題の解決にならない。

そこでこの画像ファイルをすべて埋め込んでしまうことでロード自体を無くせばよい。問題は埋め込む数。たとえば漢字の練習帳 六年生にはこの画像ファイルが1900ファイル以上ある。
画像ファイル一つは5K以下のファイルなのだが、これだけの数を埋め込もうとするとFlexもFlashも書き出し自体が出来ないケースが多い。または非常に時間がかかってしまう。以下に改善策の履歴をメモしておきます。

1.Embedで画像を埋め込む。
最初にトライしたのが、FlexのEmbedメタタグで1900枚すべての画像を埋め込んでしまう方法。
以下のようにEmbedタグを約1900回記述し書き出そうとした。

[Embed(source="assets/uid_1002_1.png")]
[Bindable]
public var uid_1002_1:Class;
[Embed(source="assets/uid_1002_10.png")]
[Bindable]
public var uid_1002_10:Class;
 ・
 ・
 ・

この方法はFlash Builderから書き出しの途中で以下のエラーになり書き出せなかった。

2.SWCにして埋め込む。
次に試みたのはSWCにして埋め込む方法。
Flashで一画毎にシンボルを定義しSWCに書き出す。Flashで名付けたリンケージ名を参照して利用する。
なお検証結果に違いはないと思いますが、約1900個のシンボルを生成するオペレーションの都合もあり画像からパスデータに変えています。

これをFlashでSWCに書き出そうとしたところ、長時間のパブリッシュ状態の後以下のエラーでSWCが出力されず。

試しに上記のアラートに指定されている、jvm.iniを編集し512MBを指定して試したのですが状況は変わりませんでした。
しかたないので約500アイテム毎に分けSWCを4ファイルにして書き出す。

今度はパブリッシュに時間がかかるも無事SWCが出力されました。
これをFlexのから使った場合も上手くいったので万事OKと思ったのですが、実機確認用の書き出しでNG。
ADL用の書き出しは時間もかからず問題ないのですが、実機用の書き出しがエラーにこそならないものの30分経過しても終わらない。試しにSWCを1ファイルだけにしたら15分位経過して出力されたので、ずっと待っていれば出力されるかもしれないのですが待ちきれずキャンセル。そもそも実機確認を繰り返す開発作業にあってこの時間は現実的じゃありません。
正確にはFlash Builderでパッケージ化の方法が「標準」の場合にこのような状況で、「高速」を指定した場合はSWCが4ファイルでも出力されたのですが最終的なリリースビルドは「標準」と同等のものなので無意味と思います。
この後Flashから書き出すSWCの書き出し方を変えていろいろ試してみたのですが、状況は変わらずで結局SWCで埋め込む方法は諦めました。

3.SWFをロードして使う。
SWCでダメならSWFをロードして使うしかないかと思いトライ。
SWCの時と同様にしてFlashで500アイテム毎に分けて4つのSWFを生成。これをロードする。

後で基本的な部分の問題に気づくのですがひとまずここではやったことをそのまま書きます。
AIR for iOSではLoaderでSWFをloadする時に以下のようにコンテキストを設定しないとロードの段階でエラーになります。

var context:LoaderContext = new LoaderContext();
context.allowCodeImport = true;

さらにロードしたSWFのクラスを参照したり親側からアクションスクリプトで操作する場合、

context.applicationDomain = ApplicationDomain.currentDomain;

も指定します。

これを試すとADL上では問題なく動く。書き出しも早いし、画像を使っていた時よりファイルサイズも小さい。今度こそ万事OKと思ったのですが、実機確認に進み状況が変わる。実機用ファイルは無事出力されるも実機確認で以下のエラー。

この原因は以下のリンクに記載がある。

モバイルデバイスでサポートされていない ActionScript 3.0 API

以下、引用です。

Loader
iPhone アプリケーションでは、Loader.load() メソッドを使用できません。Loader.load() メソッドを使用してロードされた SWF コンテンツ内の ActionScript コードを実行することができません。ただし、SWF ファイル内のアセット(ライブラリ内のムービークリップ、イメージ、フォント、音声など)は使用できます。また、イメージファイルをロードするために Loader.load() を使用することもできます。

さらに、

Loader クラスは、コンテンツが ADL で実行される場合は、特に制限なく動作します。しかし、iPhone 上で実行される場合は、ActionScript バイトコードを含む SWF コンテンツをロードしようとすると、エラーメッセージが出力されます。

SWFのロード関連でNG事項があることは頭の片隅にあったのですが、ActionScriptの実行がNGなだけだと誤解してました。
今回のSWFにActionScriptは含んでいないのですがリンケージ書き出しの時点で制限に該当する模様。となるとSWFをロードしてクラスを参照する方法は無理です。

残る手段はリンケージ書き出しせず、タイムラインに配置したインスタンスを名前で参照するしかなさそう。
これだと新規のインスタンスの生成は出来ず、配置されたインスタンスを参照して利用するしか出来ないわけだけど、今回の用途ではビットマップデータにさえ出来ればよいのでこの方法でも対応は可能。あまりスマートでない気もするのだが、やむを得ないのでこの方法にもトライしました。

1.Flashで1900個すべてのアイテムをステージに配置して名前を付ける。
2.シンボルにリンケージは設定はしない。

これをパブリッシュしてSWFをアセットとして使う。驚いたのはリンケージを設定しない場合、パブリッシュは一瞬で書き出されたこと。
これほど大量のシンボルを持たせ、一つもリンケージ書き出ししない。という条件でパブリッシュしたのは初めてかもしれなくてよく思い出せないのだけどそんなものだったっけ?なににしてもこれは嬉しい誤算なのでよしとする。

書き出したSWFをロードして利用します。この場合も前述のコンテキストを指定してロードしないとエラーになります。

var context:LoaderContext = new LoaderContext();
context.allowCodeImport = true;
context.applicationDomain = ApplicationDomain.currentDomain;

これでロードしたSWFをloader.contentより参照しステージに配置したインスタンスを名前で参照できる。
この方法では書き出しも通常の時間で無事完了しています。

ひとまず、改善したいポイント一つクリア。
あとはこれが無事林檎さんの審査通ればいいんですけども。それはまた後日。