BulkLoaderにはまった --その2--

2008/03/13 10:16 更新
ロードされる側のBaseクラスのインスタンスが生成されるかどうか(コンストラクタが呼び出されているか)について記載していなかったので追記。

結論から言うと、ロードされる側のBaseクラスは、コンストラクタが呼び出されていませんでした。


実行側Rootをnew→実行側Baseをnew→ロード側Rectをnew→実行側Baseをnew


今回の検証で、外部swfをロードする際に注意すべき事として、下記が挙げられる。
その他にもあれば是非とも教えてください!

・同じクラス名は使用しない。
・ロードされる側のドキュメントクラスではstageを参照出来ない。


前回の続きのお話です。

続きと言っても、はまった原因はFlashの仕様的な話です。

そもそも、berianが外部swfをロードするという事を今まで経験していなかった事が原因と思われる。。

以下、使用したBulkLoaderのリビジョンは186(2008/03/12現在の最新)です。


下記がロードされる側のコード。

Rect.as(ドキュメントクラス)

package {

	import flash.display.MovieClip;

	public class Rect extends MovieClip {

		public function Rect():void {

			addChild(new Base());
		}
	}
}

Base.as

package {

	import flash.display.Sprite;

	public class Base extends Sprite {

		public function Base():void {

			graphics.beginFill(0x000000);
			graphics.drawRect(0, 0, 300, 300);
			graphics.endFill();
		}
	}
}

単純に四角を描画しているだけです。

これをrect.swfとしてコンパイルして使用。


ちなみに、ドキュメントクラスRect.asがSpriteではなく、MovieClipを継承しているのは、BulkLoaderのgetMovieClipメソッドを使用して取得する事を想定しているためです。


次に、rect.swfをロードする側のコード

Root.as(ドキュメントクラス)

package {

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;

	public class Root extends Sprite {

		public function Root():void {

			stage.scaleMode = "noScale";
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.HIGH;

			addChild(new Base());
		}
	}
}

Base.as

package {

	import flash.display.Sprite;

	import br.com.stimuli.loading.BulkLoader;
	import br.com.stimuli.loading.BulkProgressEvent;

	public class Base extends Sprite {

		public function Base():void {

			var bulkLoader:BulkLoader = new BulkLoader("Bulk Test");
			bulkLoader.add('rect.swf');
			bulkLoader.addEventListener(BulkLoader.COMPLETE, completeHandler);
			bulkLoader.start();
		}

		private function completeHandler(e:BulkProgressEvent):void {

			var bulkLoader:BulkLoader = BulkLoader.getLoader("Bulk Test");
			addChild(bulkLoader.getMovieClip('rect.swf'));
		}
	}
}


単純にrect.swfをロードしてaddChildしているだけです。

これをRoot.swfとしてコンパイルして実行すると・・

Error: BulkLoader with name'Bulk Test' has already been created.
at br.com.stimuli.loading::BulkLoader()
at Base()
at Rect()


"Bulk Test"という名前でBulkLoaderインスタンスは既に作成されています!!(怒)・・と


いやいや・・ワタクシ2回もBulkLoaderコンストラクタ呼んでませんから・・

怒られる筋合いありませんから・・


デバッグしてみると、どうやらstartメソッドを実行したタイミングで、BulkLoaderコンストラクタがもう一度呼び出されているみたい。。

おかしいなぁ。。と思って、エラーメッセージをよーく見てみると、、

at Base()
at Rect()

どうもエラー発生箇所がロードされる側のswfのようです。


なぜ・・!?!?!?

もうパニックですwww


ロードされる側のBaseクラスからBulkLoaderコンストラクタが呼び出されている・・


・・・


ここでberian、やっと閃いた!!

実行するswf(Root.swf)にも、ロードされるswf(rect.swf)にもBaseクラスが存在する。

そして実行するswfのBaseクラスで、実際はBulkLoaderコンストラクタを呼び出している。


もしかして・・!!


って事で、実行するswf(Root.swf)のBaseクラスをBulkクラスにクラス名を変更。

Root.as(ドキュメントクラス)

package {

	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;

	public class Root extends Sprite {

		public function Root():void {

			stage.scaleMode = "noScale";
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.HIGH;

			addChild(new Bulk());
		}
	}
}

Bulk.as

package {

	import flash.display.Sprite;

	import br.com.stimuli.loading.BulkLoader;
	import br.com.stimuli.loading.BulkProgressEvent;

	public class Bulk extends Sprite {

		public function Bulk():void {

			var bulkLoader:BulkLoader = new BulkLoader("Bulk Test");
			bulkLoader.add('rect.swf');
			bulkLoader.addEventListener(BulkLoader.COMPLETE, completeHandler);
			bulkLoader.start();
		}

		private function completeHandler(e:BulkProgressEvent):void {

			var bulkLoader:BulkLoader = BulkLoader.getLoader("Bulk Test");
			addChild(bulkLoader.getMovieClip('rect.swf'));
		}
	}
}

処理は何も変えずにクラス名のみ変更してあります。


これをRoot.swfとしてコンパイルし、実行すると・・




おめでとーございまーす!!!




結局、何がどう悪さをしていたのかをまとめると。

【前提条件】
・Root.swfにもrect.swfにもBaseクラスが存在した。
・BulkLoaderのインスタンスを生成しているのはRoot.swfのBaseクラス。


【実際に行われた処理の流れ】
・Root.swfでBaseクラスのインスタンスが生成される。
・BaseクラスでBulkLoaderクラスのインスタンスが生成される。
・BulkLoaderインスタンスのstartメソッドを実行する。
・rect.swfがロードされる。
・rect.swfが実行され、rect.swfのRectクラス→rect.swfのBaseクラスのインスタンスが順に生成される・・
と思いきや、Root.swfのBaseクラスのインスタンスがここでもう一度生成されてしまう。

これによって、結果的にBulkLoaderクラスのコンストラクタが2回呼ばれてしまったという事だった。


いやー、、この挙動は知らなかったなぁ。。

外部swfにロードする側と同じクラス名を使ってはいけないって事か。

完全に独立して動くものかと思ってたが。。


まぁ、勉強になったという事で!!