Flex フレームワーク内部の初期化手順
MXML を利用した Flex アプリケーションを作成した際、コンパイル時および SWF ファイルを表示時にどのような処理を行われているかを調べてみました。Flex のバージョンは 2.0.1 を利用していますが、Flex 3 でもほとんど変わっていないようです。
コンパイル時に生成されるファイル
CLASSNAME.mxml をコンパイルすると、内部的にクラスがいくつも生成されます。mxmlc に -keep オプションをつけると、生成されるクラスを見ることが可能です。
CLASSNAME.mxml からは CLASSNAME-generated.as というクラスが生成されます。CLASSNAME-generated.as には CLASSNAME クラスが定義されており、MXML 中に記述したタグに相当する情報が ActionScript のソースコードに展開されています。
この CLASSNAME クラスが SWF ファイルの DocumentClass になるかと思いきや、実は違います。CLASSNAME-generated.as のメタタグ [Frame(factoryClass="_CLASSNAME_mx_managers_SystemManager")] に注目です。このメタタグにより、_CLASSNAME_mx_managers_SystemManager が1フレーム目に配置され、残りのクラスは2フレーム目以降に定義されます。
ここのフレームというのは、Flash 的なフレームのことです。Flash CS3 などでアニメーションを作成する場合、タイムライン上にフレームを並べていきますが、Flex アプリケーションは、通常2フレームの SWF ファイルとして定義されています。(--frame オプションを指定した場合には、3フレーム目以降にデータが入ることもあります。詳細は後述)
SystemManager を深く読み解く
_CLASSNAME_mx_managers_SystemManager.as には _CLASSNAME_mx_managers_SystemManager クラスが定義されています。
import mx.managers.SystemManager; import flash.utils.*; import flash.system.ApplicationDomain; import mx.core.IFlexModuleFactory; import mx.core.IFlexModule; public class _CLASSNAME_mx_managers_SystemManager extends mx.managers.SystemManager implements IFlexModuleFactory { public function _CLASSNAME_mx_managers_SystemManager() { super(); }
このクラスが DocumentClass なので、SWF のロード時には、コンストラクタが最初に呼ばれます。コンストラクタは、super() として親クラスのコンストラクタを呼んでいます。
それでは、親クラス mx.managers.SystemManager クラスのソースを見ていきましょう。
以下の流れを簡単に書いておくと、次のようになります。
- SystemManager の初期化処理
- Preloader の表示
- ロード完了後、各種 Manager や CSS の初期化
- new CLASSNAME() の実施して、SystemManager に addChild
- レイアウト実施
SystemManager のコンストラクタ
- stage を NO_SCALE TOP_LEFT に
- toplevel かどうか判定
- registerInitCallback されていれば、呼び出し
- フレーム stop (この時点での currentFrame は 1)
- loaderInfo の Event.INIT に initHandler を登録
initHandler() メソッド
1フレーム目がロード完了した瞬間に呼ばれます。
- SystemManager 一覧に自分の URL を登録
- Event.INIT イベントの登録を解除
- タイムライン上の次に実行されるフレーム(2フレーム目)に docFrameHandler() メソッドを配置
- タイムライン上のそれ以降にフレームがあるときは、extraFrameHandler() メソッドを配置
- initialize() メソッドを呼ぶ
タイムラインに関数を配置するために MovieClip::addFrameScript() メソッドを使っています。この関数は第一引数にフレーム数、第二引数に関数をとる Undocumented なメソッドです。
initialize() メソッド
- SystemManager の表示領域を取得
- Preloader のインスタンス preloader を作成
- INIT_PROGRESS イベントに preloader_initProgressHandler を登録
- PRELOADER_DONE イベントに preloader_preloaderDoneHandler を登録
- popUpChildren に preloader を追加
- info() から情報取得し、preloader.initialize 関数を呼び出す
- RSL (ランタイム共有ライブラリ)の URL やサイズ
- preloaderDisplayClass
- backgroundColor...etc
info() の実体は _CLASSNAME_mx_managers_SystemManager-generated.as で定義されており、コンパイル時に与えられた引数やメタタグの情報が格納されています。
Preloader クラスはローディングの画像を表示します。
preloader_initProgressHandler() メソッド
- INIT_PROGRESS イベントの監視をやめる
- deferredNextFrame 関数を呼び出し、次のフレームへ進む。結果として、initHandler() で定義した、docFrameHandler() メソッドが呼ばれる。
docFrameHandler() メソッド
- 各種マネージャの Singleton を作成
- ICursorManager, IDragManager, IHistoryManager, ILayoutManager, IPopUpManager, IStyleManager, IToolTipManager の7つ
- registerInitCallback されていれば、呼び出し
- Mixin メタタグつきでコンパイルされたクラスの一覧を info() から取得し、自分自身を第一引数にして init 関数を呼び出す
- AS のコードに変換された CSS 情報は Mixin メタタグつきでコンパイルされている。この場所で、CSS の登録処理が実行される。
- initializeTopLevelWindow() メソッドを呼び出して、topLevelWindow を準備する(後述)
- deferredNextFrame 関数を呼び出し、次のフレームへ進む
initializeTopLevelWindow() メソッド
ここにきて、やっと CLASSNAME クラスのインスタンスが生成されます。おなじみの CREATION_COMPLETE イベントが発行されるのもここです。
- topLevel でない場合は、parent を再帰的にたどって _topLevelSystemManager を取得する
- イベント登録
- フォーカス管理のために、MOUSE_DOWN イベントを監視
- topLevel のときは、RESIZE を監視
- create 関数で CLASSNAME クラスのインスタンスを生成する。
- create 関数はサブクラスの _CLASSNAME_mx_managers_SystemManager.as で定義されている。
- 生成したインスタンスを document, app, topLevelWindow に代入する。
- CREATION_COMPLETE イベントを発行。
- app.setActualSize() 関数でサイズ通知
- preloader registerApplication(app); で preloader に初期化完了を通知する
- 子ウインドウを追加する準備
- LayoutManager がからむ複雑な処理をして追加している
- 深追いせず
preloader_preloaderDoneHandler() メソッド
CLASSNAME オブジェクトがステージに追加された後、APPLICATION_COMPLETE イベントが発行されます。
- PRELOADER_DONE イベントの監視をやめ、preloader を popUpChildren から削除する
- mouseCatcher を作成し、addChildAt(mouseCacher, 0);
- topLevelWindow を addChildAt(DisplayObject(app), 1);
- APPLICATION_COMPLETE イベントを発行
extraFrameHandler() メソッド
追加のフレームのロード完了時に呼ばれます。追加のフレームはコンパイラオプション -frame=label,ClassName で作成できます。
extraFrameHandler() メソッド内では、ClassName で指定されたクラスの static なメソッド frame() を呼びます。この仕組みを使って、mp3 などの巨大なデータを3フレーム目以降に配置して、遅延ロードを行うことができます。
まとめ
SystemManager の処理を深追いしてみました。Flex フレームワークのソースを追うことで、Flex でよく目にする CREATION_COMPLETE イベントや APPLICATION_COMPLETE イベントなどが、実際にはどのようなタイミングで発行されているのかがよく分かります。SystemManager は複雑なのですが、メタタグを利用してできる限りシンプルな設計にしようとした努力が垣間見れて興味深くもあります。

