中級
こちらからダウンロードできます。
はじめに
UIComponent.mx_internal::dispatchEventHookを使って、コンポーネント間のイベントの流れをダンプする
Flash Debugプレーヤーの設定をしてデバッグに役立つ情報を出力させる
Flex SDKの隠れた便利コマンドを使う
Flex SDKの動作を部分的にカスタマイズする
今回は、前回に引き続きデバッグに役立つTIPSをご紹介します。
前回は「mx_internal」名前空間を使って、Flex Frameworkの内部構造にアクセスする方法を紹介しました。この「dispatchEventHook」も、UIComponent.asで定義されているmx_internal名前空間の静的変数です。(ちなみにFlex SDK 3から追加されました)
「hook」の名前からも推測できるように、この変数には「dispatchEvent」メソッドが呼ばれる直前に実行される関数を定義できます。例えば、Flexコンポーネントがどのようなイベントを発行し、どのように伝わっていくのかを明らかにするため、イベントの内容を出力するメソッドをhookに指定するなどの使用例が考えられます。
「dispatchEventHook」については正式なドキュメントには明記されていないので、UIComponent.asのソースコードで確認しましょう。以下のように定義されています(Flex SDK 3.5.0の場合)。
override public function dispatchEvent(event:Event):Boolean {
if (dispatchEventHook != null)
dispatchEventHook(event, this);
return super.dispatchEvent(event);
}
dispatchEventHookに独自の関数を割り当てることで、Flexのビジュアルコンポーネントがイベントを発行する直前に、好きな処理を実行することが出来るようになります。
dispatchEventHookにイベントをダンプする単純なメソッドを登録するだけで、コンポーネントが実行時にどんなイベントを発行し、またどのようにイベントが伝播して行くかを、ログとして出力できます。早速やってみましょう。
イベントをダンプするだけの単純なフック用のメソッドを定義し、Flexアプリケーションの初期化のタイミングで、定義したメソッドをdispatchEventHookに指定してみます。
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:applicationComplete>
<![CDATA[
UIComponent.mx_internal::dispatchEventHook = myEventHook;
]]>
</mx:applicationComplete>
<mx:Script>
<![CDATA[
import flash.utils.getTimer;
import mx.core.UIComponent;
private function myEventHook(e:Event,comp:UIComponent):void {
trace(e, getTimer());
}
]]>
</mx:Script>
<mx:Button label="button"/>
</mx:Application>
実行してみましょう。
ボタンをクリックすると、「buttonDown」「updateComplete」などのイベントがログ出力されます。
[Event type="updateComplete" bubbles=false cancelable=false eventPhase=2] 579
[Event type="updateComplete" bubbles=false cancelable=false eventPhase=2] 2855
[Event type="buttonDown" bubbles=false cancelable=false eventPhase=2] 3565
ボタンを操作する際に何が起きているのかを、イベントを通じて把握することが出来ます。
ところで、クリックした場合に出力されるはずの「mouseDown」や「click」などのイベントが、ここでは出力されていない事にお気づきの方もいるでしょう。これは、「mouseDown」や「click」が、Flash Playerが直接発行するイベント(MouseEvent)であるためです。
(※ちなみに、Flash Playerが発行するイベントは、クラスのパッケージ名で判別できます。パッケージ名が”flash.events..”で始まっていれば、そのイベントは*基本的には*Flash Playerが発行します)
dispatchEventHookメソッドでフックできるのは、UIComponentとその派生クラスであるコンポーネントが発行するイベントだけです。注意しましょう。
イベントの流れをログ出力することが何の役に立つのか、今一つイメージが湧かないかも知れないので多少補足します。
例えば、カスタムコンポーネントを作成したものの、挙動がおかしい(重すぎる等)とします。
この時、予期しないイベントのループが起こっていないかの判定のために、カスタムコンポーネントが発行する全てのイベントを、dispatchEventHookを使うことで出力し、判定の材料とすることができます。
また、(複合的な)処理の実行時間の計測をする際にも役に立ちます。例としてチャートについて考えてみましょう。チャートの表示に掛る時間を計測したいとします。
チャートコンポーネントに表示するデータを(dataProvider経由で)設定してから、実際に表示されるまでは、一つのメソッドで完結する訳ではなく、非同期に複数の処理が走るため、実はこれはなかなかやっかいな問題です。どこかに計測ポイントを設定すれば済む問題ではないからです。
コンポーネントへのデータのセット、関連するコンポーネントへのイベントによる通知、…が発生し、最終的に、チャートコンポーネントの描画完了イベント(「updateComplete」イベント)が発生します。
この間に発行される全てのイベントをダンプすることで、処理の開始から終了までに実際に掛った時間を洗い出すことが可能になります。
ところで、dispatchEventHookに適用できるメソッドを含むライブラリ(.swc)を用意しました。
最後のダウンロードの項をご参照下さい。
Flash Playerのデバッグ版を利用することで、実行時にさまざまな情報を出力することができます。
通常Flash Builderをインストールする際に、デバッグ版のPlayerもインストールされますが、下記のURLから個別に入手してインストールすることも可能です。
http://www.adobe.com/support/flashplayer/downloads.html

リリース版のFlash Player同様、Internet Explorer用とそれ以外のブラウザ用の2種類がありますので、適切な方(あるいは両方)をダウンロード、インストールして下さい。
デバッグ版のFlash Playerの挙動を設定するには、「mm.cfg」というファイルを作成する必要があります。詳細については以下のURLで解説されている通りですが、ここでは設定方法を抜粋して紹介します。
「デバッガ版の Flash Player の構成」 (Adobe Flex 3 ヘルプ)
http://livedocs.adobe.com/flex/3_jp/html/help.html?content=logging_04.html
始めに、以下の場所にmm.cfgを作成します。
| Windows 2000 / XP | C:¥Documents and Settings¥<ユーザ名> |
|---|---|
| Windows Vista | C:¥Users¥<ユーザ名> |
| Macintosh OS X | /Users/<ユーザ名> 又は /Library/Application Support/Macromedia |
それでは、mm.cfgの設定例を見ていきます。
mm.cfgに以下を記述します
ErrorReportingEnable=1
TraceOutputFileEnable=1
trace関数の呼び出しを含むSWFを実行すると、trace関数に指定した文字列が「flashlog.txt」という名前のログファイルに出力されます。出力先は以下の表の通りです。
ログファイルの出力先:
| Windows 95/98/ME/2000/XP | C:¥Documents and Settings¥<ユーザ名>¥Application Data¥Macromedia¥Flash Player¥Logs |
|---|---|
| Windows Vista | C:¥Users¥<ユーザ名>¥AppData¥Roaming¥Macromedia¥Flash Player¥Logs |
| Macintosh OS X | /Users/username/Library/Preferences/Macromedia/Flash Player/Logs/ |
オブジェクトの構造を出力したい時は、Flex SDKのmx.utils.ObjectUtil.toString()を呼ぶことで詳細な出力が得られます。
例:
import mx.utils.ObjectUtil;
var complexStructure:Object = {/* … */ }
trace(ObjectUtil.toString(complexStructure));
また、flash.utils.getTimer()を呼ぶことで、Flash Playerが実行を開始した時点からのタイムスタンプが得られます。
例: 簡単なベンチマークを取る:
import flash.utils.getTimer;
const timestamp:int = getTimer();
longlongOperation();
trace(getTimer() - timestamp); // 処理にかかった時間を出力 (ミリ秒)
mm.cfgに以下のように記述してください。
AS3Trace=1
AS3Verbose=1
Flash Virtual Machineが実行した命令が出力されます。
膨大なログが吐かれ、ログ出力の処理時間が増大するため、大きなSWFファイルを実行するとあたかもFlash Builderがフリーズしたかのようになる場合があります。注意して使ってください。
TraceOutputBuffered = 1
見落としがちですが、デバッグに役立つ機能です。
デバッグ版 Flash Playerの実行時に、右クリックしてコンテキストメニューから「再描画領域を表示」を選択することで、再描画領域が赤い枠で表示されます。
この設定を行うことで、不要な、あるいは不自然な領域での再描画が発生していないか、実装者以外も確認が可能ですので、テストの際などに細かく確認することをお勧めします。
作成したカスタムコンポーネントの動作が重い場合などに、無駄な再描画をしていないかについては案外判別しにくいので、再描画領域を表示するだけでも思わぬ発見に繋がることがあります。
SWFにコンパイルされた命令セットも表示できる「swfdump」
「swfdump」は、SWFファイルの詳細な情報を出力する、コマンドラインツールです。
SWFファイルの構造をXMLで出力する機能、SWFファイル内にコンパイルインされたバイトコードの命令セットを、アセンブリ形式で表示する機能などがあります。
「swfdump」は、Flex SDK 3系の配布物の中には含まれていませんでしたが、Flex SDK 4(4.0.0.14159で確認)には含まれていますので、Flex SDK 4をインストールし、そのSDKディレクトリの「bin」ディレクトリにパスを通すことで使用できます。
(Flex SDK 3系では、SDKソースコード一式をAdobeのリポジトリから取得してビルドすることによって、swfdumpを生成できました)
コマンドライン引数に、情報をダンプしたいSWFファイル、又はSWFファイルのURLを指定することで、SWFの情報がXMLで出力されます。
コマンドラインオプションを組み合わせることで、出力される項目を削除・追加できます。例えば、「-abc」オプションを付けることで、SWFファイル内のバイトコードをディスアセンブルするよう指定できます。
swfdumpに指定できるオプション:
C:¥devel¥flex¥sdk¥4.00.145159> swfdump
Usage: java tools.SwfxPrinter [-encode] [-asm] [-abc] [-noactions] [-showdebugsource] [-showoffset] [-noglyphs] [-external] [-save file.swf] [-nofunctions] [-out file.swfx] file1.swf ...
SWFをコンパイルするのに使ったFlex SDKのバージョン、フレームレート、指定されたメタデータなどを確認することが可能です。また出力形式がパースし易いXMLですので、作成(・デプロイ)したFlexアプリケーションが意図した通りかどうかの検証を、外部スクリプト等との組み合わせで自動化する、などの利用方法が考えられます
少々余談になりますが、「-abc」オプションによるディスアセンブリの出力について、筆者は、開発したFlexアプリケーションで問題があった際の原因究明の際に利用し、助かった経験があります。
あるFlexアプリケーションをデバッグビルドした際には動作に問題がなかったものの、(全く同じソースコードに対して)リリースビルドを行うと、実行時にエラーが発生してしまう、ということがありました。
それで、両方のビルドで生成されたSWFに対し、それぞれ「swfdump -abc」を実行して、生成された命令セットの差分を比較しました。
その結果、実行時エラーとなったメソッドで、ビルド間で命令セットの差異が生じていることが確認でき、問題の原因が特定できました。(結局、問題の原因として特定したメソッド内での実装方法を調整することで解決)
参考までに、swfdumpによる出力例を以下に示します。
swfdumpによる出力例:
<!-- Parsing swf file://C:/hello.swf -->
<!-- ?xml version="1.0" encoding="UTF-8"? -->
<swf xmlns='http://macromedia/2003/swfx' version='9' framerate='24' size='10000x7500' compressed='true' >
<!-- framecount=1 length=858 -->
<FileAttributes hasMetadata='true' actionScript3='true' suppressCrossDomainCaching='false' swfRelativeUrls='false' useNetwork='true'/>
<Metadata>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'><rdf:Description rdf:about='' xmlns:dc='http://purl.org/dc/elements/1.1'><dc:format>application/x-shockwave-flash</dc:format><dc:title>Adobe Flex 3 Application</dc:title><dc:description>http://www.adobe.com/products/flex</dc:description><dc:publisher>unknown</dc:publisher><dc:creator>unknown</dc:creator><dc:language>EN</dc:language><dc:date>2010/05/20</dc:date></rdf:Description></rdf:RDF>
</Metadata>
<ScriptLimits scriptRecursionLimit='1000' scriptTimeLimit='60'/>
<SetBackgroundColor color='#869CA7'/>
<ProductInfo product='Adobe Flex' edition='' version='3.5' build='12683' compileDate='10/05/20 13:15'/>
<FrameLabel label='Hello'/>
<DoABC2 name='frame1'>
</DoABC2>
<SymbolClass>
<Symbol idref='0' className='Hello' />
</SymbolClass>
<ShowFrame/>
</swf>
例えば、カスタムコンポーネントを開発する際などに、
といった場合があると思います。そのための手順を紹介します。
比較的簡単な手順で試してみることができる方法です。
本格的に行うには、Flex SDKの公開リポジトリからソースコード一式を手に入れてリビルドし、作成したswcをプロジェクトで使用します(手順は後述します)。
例として、UIComponent.asのカスタマイズをしてみましょう。
UIComponentカスタマイズ時のプロジェクト構成:
上記をより本格的に行うためには、Flex SDKをリビルドしてライブラリ(swcファイル)を作成し、プロジェクトのリンク設定で、ビルドしたライブラリを指定します。
Flex SDKのクラスファイルをカスタマイズする場合は、都度ライブラリをビルドします。
Flex SDKのソースリポジトリは、以下になります。(Subversionで運用されています)
http://opensource.adobe.com/svn/opensource/flex/sdk/
まずは、Subversionのクライアントをインストールし、リポジトリからソースコードをチェックアウトしましょう。Windows環境であれば、TortoiseSVNやCygwinをインストールするのが手軽です。
では、早速Flex SDK 3系の最新版が開発されているブランチ「branches/3.x」を取得してみます。
Cygwinでの例になります。
C:¥> mkdir c:¥devel¥flex¥sdk¥branches
C:¥> cd C:¥devel¥flex¥sdk¥braches
C:¥devel¥flex¥sdk¥branches> svn co http://opensource.adobe.com/svn/opensoruce/flex/sdk/branches/3.x/
※注 実際には一行です。改行は入力しないで下さい
ソースコードに加えてFlash Player本体等も含まれるため、ファイルサイズの合計は410M程と大きいです。全てチェックアウトするまでには10分程度かかるのではないでしょうか。
Flex SDKのビルドには「Apache Ant」と「Java 開発キット(JDK)」が必要ですので、事前にインストールしてください。ビルド自体は簡単で、SDKのトップレベルのディレクトリに移動して、「ant」を実行するだけです。
C:¥devel¥flex¥sdk¥branches> cd 3.x
C:¥devel¥flex¥sdk¥branches¥3.x> ant
これで一式ビルドされます。あとは、作成されたライブラリファイル(*.swc)を、プロジェクトのリンク設定に指定すれば完了です。
参考までに、テストプロジェクトを作成して標準のリンク設定を変更し、ビルドしたライブラリファイルを参照させた時のスクリーンショットです。
加えて、Flex Frameworkのクラスに対してブレークポイントを設定できるように、Flex Frameworkのトップレベルのフォルダーへの参照(例: 「mx」)もプロジェクトに加えると更に便利でしょう。
Eclipseでフォルダーへのリンクを作成するには、メニューから[File]-[New]-[Folder]と選択し、表示されたダイアログの左下にある「Advanced >>」ボタンをクリック、リンク先のディレクトリをダイアログで選択することにより行えます。
普段、あまり具体的に扱うことのない基底クラス、例えばUIComponentやListBaseの構成や実行時の振る舞いを理解するために、これらのクラスの小規模なカスタマイズを加えて動かしてみるのは有効です。是非お試し下さい。
デバッグに役立つTIPSを紹介してきましたが、いかがでしたでしょうか。
Flexアプリケーション開発の一助となれば幸いです。
ささやかながら、冒頭で紹介したdispatchEventHookを登録してイベントフローをダンプするメソッドをライブラリ化しました。 ライブラリを使ってイベントフローをダンプするには、プロジェクトの「libs」ディレクトリに「bluehorizon.swc」をコピーし、コンパイラ設定に「-includes+=jp.classmethod.bluehorizon.debug.EventDumper」を追加して下さい。発生したイベントをインデントしてtrace出力します。