最近、告知と勉強会エントリばっかりでしたが、タイトル通り、ActionScript でクラス置換風なことが出来ることに気付いたので、まとめてみます。この手法を使うと、任意の SWF 内で使用されているクラスを、自分で用意した別のクラスで置き換えて実行することが出来ます。ポイントは、その任意の SWF をパブリッシュしなおしたりだとか、バイトコードでどうこうだとかそういったハックの類を使わなくてよく、一切弄る必要がないところです。
サンプルを Spark project にコミットしたので、これを見ながら読み進めて下さい。
まず、以下のような Context クラスと DocumentRoot クラスで構成された、contents.swf があります。
src/org/libspark/sample/Context.as
package org.libspark.sample
{
/**
* アプリケーションに必要な情報を保持します
*
* @author yossy:beinteractive
*/
public class Context
{
public static function get message():String
{
return "test";
}
}
}
src/org/libspark/sample/DocumentRoot.as
package org.libspark.sample
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
/**
* 実行時クラス置換のサンプル
* 読み込まれるコンテンツのドキュメントルートです
*
* @author yossy:beinteractive
*/
public class DocumentRoot extends Sprite
{
public function DocumentRoot()
{
var field:TextField = new TextField();
field.autoSize = TextFieldAutoSize.LEFT;
field.text = Context.message;
addChild(field);
}
}
}
見ての通り、Context.message プロパティの内容をテキストフィールドに表示しているだけです。contents.swf を見てみる と、予想通り、「test」が表示されます。ここまでは普通ですね。
続いて、この contents.swf を Loader で読み込んで表示するだけの、en.swf と、jp.swf を見てみて下さい。見れば分かると思いますが、contents.swf を読み込んで表示しているのであれば test と表示されるかと思いきや、それぞれ「hello」と「こんにちは」と表示されていますね。一体どうやっているのでしょう?
と、もったいつけてみますが、ここでクラス置換が登場します。実は、en.swf と jp.swf には、以下のような Context クラスが埋め込まれています。
src-en/org/libspark/sample/Context.as
package org.libspark.sample
{
/**
* 英語版アプリケーションに必要な情報を保持します
*
* @author yossy:beinteractive
*/
public class Context
{
public static function get message():String
{
return "hello";
}
}
}
src-jp/org/libspark/sample/Context.as
package org.libspark.sample
{
/**
* 日本語版アプリケーションに必要な情報を保持します
*
* @author yossy:beinteractive
*/
public class Context
{
public static function get message():String
{
return "こんにちは";
}
}
}
それぞれのクラスで、message プロパティの値が変わっていますね。そして、この英語版 Context クラスと、日本語版 Context クラスを、contents.swf の実行時に、元々存在してた Context クラス (message プロパティの値が test な Context クラス) と入れ替えることで、表示されるメッセージの内容を切り替えていたのです。これが、クラス置換です。
さて、やってみれば分かると思いますが、en.swf と jp.swf に置き換えたい Context クラスを埋め込んだからといって、普通に contents.swf を読み込んだだけでは、クラス置換は実現出来ません。ここで、ApplicationDomain というヤツが登場します。
通常、親の SWF が読み込んだ子 SWF が、親 SWF で定義されているクラスにアクセス出来てしまうと、色々と悪さをされる可能性があるので、親 SWF のスクリプトと、子 SWF のスクリプトは、別の空間で実行され、互いに参照出来ないようになっています。この「空間」に相当するのが ApplicationDomain です。逆に、同じ空間 (=同じ ApplicationDomain) で親 SWF と子 SWF を実行すると、お互い定義されているスクリプトやクラスを参照出来るようになります。ちなみに、このとき、親 SWF と子 SWF で同じクラスが使われていて、コンフリクトする、という事態が想定出来るわけですが、FlashPlayer の仕様では、このようなとき、エラーになるのではなく、親 SWF で使われているクラスが優先され、子 SWF のクラスは無視されるという動作をします。
ここまでくれば気付いた人も多いと思いますが、この、子 SWF を同じ ApplicationDomain に読み込むとき、親 SWF で定義されているクラスが優先される、という動作を利用して、今回のクラス置換を実現しています。en.swf と jp.swf の ApplicationDomain には、既に英語版もしくは日本語版の Context クラスが存在するため、contents.swf を同じ ApplicationDomain に読み込んだとき、それらのクラスが使われる、というわけです。
実際の、en.swf と jp.swf のドキュメントルートのコードは以下のようになります。
src-en/org/libspark/sample/ParentDocumentRoot.as
src-jp/org/libspark/sample/ParentDocumentRoot.as
package org.libspark.sample
{
import flash.display.Sprite;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.system.ApplicationDomain;
/**
* 実行時クラス置換のサンプル
* コンテンツを読み込むドキュメントルートです
*
* @author yossy:beinteractive
*/
public class ParentDocumentRoot extends Sprite
{
public function ParentDocumentRoot()
{
// 置換したいクラスを参照することで必ずそのクラスが SWF に埋め込まれるようにします
Context;
// コンテンツをカレントドメインに読み込みます
var loader:Loader = new Loader();
addChild(loader);
loader.load(new URLRequest('contents.swf'), new LoaderContext(false, ApplicationDomain.currentDomain));
}
}
}
Loader クラスを使って load するときに、LoaderContext を使って、ApplicationDomain.currentDomain (=現在のドメイン = 同じ ApplicationDomain) に読み込まれるようにするのがポイントです。
というわけで、今回のクラス置換の方法をまとめると、
- 置換したいクラスと同じパッケージ、クラス名で、置換先のクラスを用意する
- ドキュメントルートなどでそのクラスを参照し、必ず SWF に埋め込まれるようにする
- Loader::load(new URLRequest('/path/to/swf.swf'), new LoaderContext(flase, ApplicationDomain.currentDomain)) を使用して、SWF を同じ ApplicationDomain に読み込むことにより、置換する
今回のサンプルのように、ローカライズに使用したり、バリエーションを作らなければならない場合に設定をひとつのクラスにまとめて置換したり、ハックに使用したり、色々と応用が効くと思うので、ぜひ活用してみて下さい〜。
今月も Spark project 勉強会 #05 を終えました。on the fly の話とか、相変わらず濃くて素敵でした〜!
以下、僕の発表資料です。
- Spark project 勉強会 #05 Keynote (近況報告)
- Pixel Bender と戯れる
去年は一般参加だった Adobe MAX ですが、今年は Spark project として枠を頂けました。やたー!何をやるか詳しくは以下で。
閃光的網站・弛緩複合体さん が、しばらくそうめんのことについて記事を書いて下さっていたのですが、そうめんのエッセンスをとてもよく理解して下さっている素敵な内容なので、まだ読んでいない方はぜひ読んでみて下さいませ。いまのところ、(1)〜(10) までアップされています。ありがとうございますありがとうございます。
- http://aquioux.blog48.fc2.com/?tag=%A4%BD%A4%A6%A4%E1%A4%F3 (「そうめん」タグが付いた記事一覧)
拙作 ActionScript Thread Library 1.0 (そうめん) の制作事例に、追加し忘れてた大富豪と dotFes Clock (どちらも BeInteractive! 制作という若干の悲しさ) と、そして、今日事例として送られてきた、噂のアイツ、「CUBACLOCK」(これ地味に好きです) を追加しました!これで外部案件が二個になりましたよ。めでたい。
詳しくは以下の URL でどうぞ。
「三度の飯より TAKESAKO メソッド」でお馴染みの id:TAKESAKO さんから紹介を受けて、Web Career 様 の「Engineer25」というコーナーにインタビューが掲載されました。良かったら見てくださいまし。
今月も無事、Spark project 勉強会 #04 を終えました。来月もやる予定なので、 公式ブログ をチェックしていて下さい。
以下、僕の発表資料です。
- Spark project 勉強会 #04 Keynote (近況報告)
- Making of "dotFes Clock"
あと、Keynote でも触れて、公式ブログにも書いたのですが、Spark project が英語化しつつあります。ぜひ見てみて下さい & 協力して下さい〜!



