-compiler.external-library-pathで始めるSWF分割術
こんにちは!taroです.
前回2回に渡り、flex-configファイルの書き方や、オプションの調べ方、flex-configのタグの命名規則等まとめてみました.
今回からは、より実用的なコンパイラ・オプション指定方法について考えていきます.
早速ですが・・・mxmlcには-compiler.external-library-pathというオプションがあります.
これはどういったものでしょうか?
-helpオプションを使って調べます.
mxmlc -help extern
このようにオプションの一部でも検索できます.
-compiler.external-library-path [path-element] [...] エイリアス -external-library-path コンパイル対象であるが、リンクからは除外される SWC ファイルまたはディレクトリの一覧 (反復可能)
僕の場合(Flex SDK 4.1)このような表示でした.
リンクについては、wikiのページなんかを参考にされると良いですが、つまりこのオプションを指定すると、
「このオプションで指定されたswc(もしくはディレクトリに含まれるswc)内で定義されるクラスは、コンパイルはされるが、
コンパイルされたバイトコードは出力されるswfに含まれない」
ということになります. swfを分割するに役立ちそうです!
swfを分割すると?
- 音であったり、グラフィック、フォント等を別サーバーにおける. (サーバーの転送量・帯域等を気にする場合は重要ですね.)
- プリローダーや取りあえず一番最初に必要なものを表示させてアプリ全体のロードまでの間を持たせることも可能.
- さらに大規模なアプリでは複数間のswfで共通に使用されるライブラリを分けることで、全体のファイルサイズを軽減できる.
実際にやってみる
Flashで書きだしたswcをexternal-library-pathに指定することをイメージして作りました.
サンプルプロジェクトはこちらからダウンロードできます.
fl.controls.TextAreaをFlashからbin/design.swc, bin/design.swfに書き出します.
Flashからのswc書き出しについては、弊社堀口のSWC書き出しを有効に使って作業効率アップ、FlashIDEでのクラス名指定方法いろいろなんかがオススメです.
プロジェクトの構成は下の図のようになります.

fl.controls.TextAreaが正しくコンパイルされるかみてみます.
package { import fl.controls.TextArea; import flash.display.Sprite; public class EmbedTest extends Sprite { public function EmbedTest() { initView(); } private function initView():void { var ta:TextArea = new TextArea; ta.setSize(190, 90); ta.move(5, 5); ta.text = 'fl.controls.TextArea'; addChild(ta); } } }
configファイルを書きます.
external-library-pathにdesign.swcが含まれるフォルダを指定します.
<?xml version="1.0" encoding="utf-8" ?> <flex-config> <compiler> <debug>false</debug> <external-library-path> <path-element>../bin</path-element> </external-library-path> </compiler> <output>../bin/test.swf</output> <static-link-runtime-shared-libraries>true</static-link-runtime-shared-libraries> </flex-config>
早速コンパイルしてみます.
mxmlc EmbedTest.as
僕のローカルでは843バイトのswfが出来ました. 早速ブラウザ等でプレビューしてみます.
VerifyError: Error #1014: クラス fl.controls::TextArea が見つかりません。
実行時エラーが出ました. コンパイラの説明通りコンパイルはされるけれど、fl.controls.TextArea自体はswfには書き出されていないようです.
試しにexternal-library-pathではなくlibrary-pathとしてみます.
<flex-config> <compiler> <debug>false</debug> <library-path> <path-element>../bin</path-element> </library-path> </compiler> <output>../bin/test.swf</output> <static-link-runtime-shared-libraries>true</static-link-runtime-shared-libraries> </flex-config>
今度は大きなswfが生成されます. 実行してみると今度は表示されます.
external-library-pathを指定するとswfにはライブラリのバイナリが書き出されないことは分かりました. しかし・・・
まともに動くswfを作るには?
ランタイムエラーは定義が見つからないということですので、ApplicationDomainにこの書き出されなかったライブラリを読み込んでおけばよさそうです.
LoaderContextを指定すればこの指定は出来ました.
先ほどのコードにloadDesign()というメソッドを追加しまして、ライブラリがロードされるのを待ってから実行します.
package { import fl.controls.TextArea; import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.net.URLRequest; import flash.system.ApplicationDomain; import flash.system.LoaderContext; public class EmbedTest extends Sprite { public function EmbedTest() { loadDesign(); } private function loadDesign():void { var ldr:Loader = new Loader; ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, complete); ldr.load(new URLRequest('design.swf'), new LoaderContext(true, ApplicationDomain.currentDomain)); } private function complete(e:Event):void { initView(); } private function initView():void { // TextAreaの定義が外部ファイルであることは意識する必要が無い!! var ta:TextArea = new TextArea; ta.setSize(190, 90); ta.move(5, 5); ta.text = 'fl.controls.TextArea'; addChild(ta); } } }
今度は上手く動いているようです.
手順のまとめ
- external-library-pathで分割してしまうswfに入れるクラスが含まれているswcを指定する.
- 分割してしまったswfはそのままだと動かない.
- loader.loadの第二引数を指定し、ApplicationDomain.currentDomainにライブラリ化したswfを読み込む.
- ライブラリ化したswfが読み込んだ後は、普通のimportしているときと同じような書き方をすればよい.
この方法の利点
- ApplicationDomain.getDefinition()やgetDefinitionByName()でクラスへの参照をとって・・・という必要はない.
- グラフィックなどについては、swfとして親子関係を作るということも出来ますが、この方法だと、ActionScriptだけで書かれたライブラリのようなロジック部分も共通化が容易.
- 構文的には普通にimportして使っているのと同じなので、swfを分割するつもりがなく作っていたようなプロジェクトでも大きな変更点を加えずに分割できる.