ランタイム共有ライブラリのさらなるローディング方法

  • 2011 年 5月 28 日
  • kosuke

Adobeのヘルプからもリンクが貼られているブログ記事で、Using a Custom Preloader Loop With TLF Textというエントリーを読みました。

TLFテキストは符号付きRSLが用意されていますが、独自のプリローダーで読込もうとするといろいろなエラーが出てうまく読込めません。
その対処法として、ActionScriptのクラスを書き出すフレームを2フレームにして、1フレーム目ではフレームスクリプトを記述し、RSLとSWF本体の読込みが完了したら2フレームに移動する方法が書かれています。1フレーム目にプリロードを持たせるってのは、昔からやってる人なら懐かしく思う方法なんじゃないでしょうか。

なるほど。そういう手もあるかと思いまして、前回のエントリでは、単体SWFでRSLを読込む時、Event.INITが発生するまで、SWF自身の容量を取得できませんでしたが、この方法ならいけるんじゃないかと。

しかも、先のページのサンプルだと、フレームスクリプトで、

this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

なんて記述があって、えぇ?!そんなところでRSLの進行具合取れるの?と驚いたりもします。
ということで試してみました。今度もまた入り組んでいてわかりにくいのですが。

1.SWZをカスタムプリローダーで読込んでエラーを確認
まずサンプルをそのまま試してみます。1フレームにTLFTextFieldを配置。

ひとまずエラーを確認する為、クラスを書き出すフレームは1のままでカスタムプリローダーを使う設定だけします。

パブリッシュ。Flash Player 10 & 10.1をターゲットにしました。ご覧のようなエラー出る。コンパイル段階でも警告が出てます。そして1フレームに配置したTLFTextFieldが表示されません。このエラーはプリローダーを利用しない「コードにマージ」をした場合はもとい、デフォルトのプリローダーSWFを使った時は発生せず、TLFTextFieldも表示されることを確認しています。つまり、このエラーはカスタムプリローダーを設定したとき特有のもの。

2.jeffkamerer.comのフレームスクリプトを入れて確認
次にjeffkamerer.comの方法でエラー解消を確認する。TLFTextFieldを2フレームに移動。

クラスを書き出すフレームを2に変更。

パブリッシュすると2フレームで無事TLFTextFieldが表示される。traceは以下の通り。

1:Frame Script Start
ProgressEvent.PROGRESS: 36092 36092
Event.COMPLETE: 36092 36092
RSLEvent.RSL_PROGRESS: 1208 186404
•
•
•
RSLEvent.RSL_PROGRESS: 186404 186404
RSLEvent.RSL_LOAD_COMPLETE: 0 0

SWF自身のProgressEvent.PROGRESSが取得できている。SWF自身の読込みが完了してからRSLEvent.RSL_PROGRESSが動き始めているように見えるけど、これはSWFの容量が小さいからで、容量を大きくして、ダウンロードのシュミレートで見るとProgressEvent.PROGRESSとRSLEvent.RSL_PROGRESSは平行して実行されています。

3.通常のRSLを加えてみる
SWZのTLFを読込む確認ができたので、ここにRSLの読込みを加えてみる。前回のエントリーで使った、写真の枠のシンボル、PhotoTemplateをRSLに書き出したShare.swfを使います。
まず、SWFのライブラリにPhotoTemplateをRSL読込み設定をする。そのシンボルを2フレームに配置しました。

これをパブリッシュすれば2フレームに移動した後、Share.swfのRSLが表示されると思ったのですがダメでパブリッシュするとエラー。

これなんだか意味不明なエラーなんですけど。エラーに関していえばこれに始まったことではなく他にも不思議なエラーいろいろ出ますので…。そもそもTLFが普通にカスタムプリローダーで読込めないって話自体、意味不明なわけでこの位で挫けていてはRSLは使えません。
ここはひとまず、2フレームに配置したPhotoTemplateのインスタンスを削除します。そして前回同様、PhotoTemplateを入れ子にしたシンボル(PhotoContainer)をつくりActionScriptに書き出すとします。さらにこのままだと2フレームにPhotoTemplateは表示されないので、2フレームのフレームスクリプトでインスタンスを表示させるスクリプトを加えました。

import share.PhotoTemplate;
 
var photo:PhotoTemplate = new PhotoTemplate();
photo.x	= 250;
photo.y	= 300;
addChild( photo );

これでパブリッシュすると、エラーはでないのですが正しく動かない。1フレームから動かない状態になる。ブラウザでみるとShare.swfも読込んでいるし、trace結果はEvent.COMPLETEもRSLEvent.RSL_LOAD_COMPLETEも動いているのでgotoAndStop(2)が動かないという状況。ちなみに2フレーム目にいれたフレームスクリプトはこの不具合に関係ありません。消しても同様だった為。
もうこのあたりから、何が仕様で、正しく動いているのか、バグなのか、こちらのエラーなのかわからなくなってくるのですが…。
ひとまず解決策として、入れ子にしたPhotoContainerのActionScript書き出しで「2フレーム目に書き出し」のチェックを外します。

パブリッシュすると、今度は2フレーム目まで動き、TLFTextFieldが表示されます。しかし、2フレーム目に書いたフレームスクリプトインスタンスを追加してもShare.swfのもつRSLが表示されません。ブラウザで確認してみるとShare.swfを読込んでいない状況です。

4.RSLPreloaderを加えてみる
3の結果からそれなら前回使ったRSLPreloaderを使ってあらかじめShare.swfを読込んだらどうだろうと思いました。
その前に気になることがあります。1フレームに書いた以下のフレームスクリプトでSWZの読込みはイベントとして取得できています。

addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

だったら、通常のRSLも同じようにイベント取得できないか?
試しに3の状態からTLFTextFieldを削除して、1フレームにPhotoTemplateを配置してみたのですが…。表示はされるもののイベントは取得できずでした。また前回と同じでEvent.INITが発生するまでSWF自身の容量がわかりません。

更にPhotoContainerの「2フレーム目に書き出し」にチェックを入れた場合でも同様。この場合は2フレーム目に入らないのでShare.swfも読込まれないという状況です。

つまり、フレームスクリプトで書いた、

addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

で取得できるのは、SWZの場合のみのようです。SWZはユーザーがローカルへのキャッシュを許可していない場合、フェイルオーバー RSLという通常のRSLが代替えで読込まれますがこのRSLは上記のイベントで取得できました。(ややこしい。)

なので、符号付きRSLもRSLも読込む場合、符号付きRSLのプリロードは上記のイベントで取得し、RSLはRSLPreloaderでプリロードするってことになるんですかね?ともあれRSLPreloaderを試してみます。

RSLPreloaderでShare.swfを読込む。読込みの完了がどういう順で起きるかはわからないので、RSLPreloaderの完了フラグとしてpreCompleteを追加し、swfComplete と rslComplete をあわせて3つのフラグが処理されたらフレーム2に移動させる。

import fl.events.RSLEvent;
import fl.rsl.RSLInfo;
import fl.rsl.RSLPreloader;
 
import flash.events.Event;
import flash.events.ProgressEvent;
 
trace( "1:Frame Script Start" );
stop();
 
var rslPreloader:RSLPreloader;
var info:RSLInfo
 
var swfComplete:Boolean = false;
var rslComplete:Boolean = false;
var preComplete:Boolean = false;
 
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
loaderInfo.addEventListener(Event.INIT, onInit);
loaderInfo.addEventListener(Event.COMPLETE, onComplete);
 
addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
 
rslPreloader= new RSLPreloader();
info		= new RSLInfo();
info.addEntry( 'Share.swf' );
rslPreloader.addRSLInfo( info );
rslPreloader.addEventListener( RSLEvent.RSL_PROGRESS, onRslProgress );
rslPreloader.addEventListener( RSLEvent.RSL_LOAD_COMPLETE, onRslLoadComplete );
rslPreloader.start();
 
 
//SWFのロード確認リスナー
function onProgress(e:ProgressEvent):void{
	trace( "ProgressEvent.PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onInit(e:Event):void{
	trace( "Event.INIT:", e.target.bytesLoaded, e.target.bytesTotal );
}
 
function onComplete(e:Event):void{
	trace( "Event.COMPLETE:", e.target.bytesLoaded, e.target.bytesTotal );
	loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
	loaderInfo.removeEventListener(Event.COMPLETE, onComplete);
	swfComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}
 
//RSLのロード確認リスナー
function onRSLProgress(e:RSLEvent):void{
	trace( "RSLEvent.RSL_PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onRSLComplete(e:RSLEvent):void{
	trace( "RSLEvent.RSL_LOAD_COMPLETE:", e.bytesLoaded, e.bytesTotal );
	removeEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
	removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}
 
//RSLPreloaderのロード確認リスナー
function onRslProgress( e:RSLEvent ):void{
	trace( "RSLPreloader RSL_PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onRslLoadComplete( e:RSLEvent ):void{
	rslPreloader.removeEventListener(RSLEvent.RSL_PROGRESS, onRslProgress);
	rslPreloader.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRslLoadComplete);
	trace( "RSLPreloader RSL_LOAD_COMPLETE:", e.bytesLoaded, e.bytesTotal );
	preComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}

これでやっと期待どおりに動いた。

traceを確認すると以下でした。

1:Frame Script Start
Event.INIT: 17931 17931
ProgressEvent.PROGRESS: 17931 17931
Event.COMPLETE: 17931 17931
RSLPreloader RSL_PROGRESS: 1024 3613
・
・
RSLPreloader RSL_PROGRESS: 3613 3613
RSLPreloader RSL_LOAD_COMPLETE: 0 0
RSLEvent.RSL_PROGRESS: 1208 186404
・
・
RSLEvent.RSL_LOAD_COMPLETE: 0 0

Progressが順番に並んで発生していますが、これもダウンロードのシュミレートで確認するとバラバラに発生するので、読込みが完了する順番は必ずしも一定ではありません。

さて、これで完璧と思ったのですが落とし穴が他にもありました。
それはドキュメントクラス。この状態でドキュメントクラスを設定しました。内容は以下。

package sample{
 
	import flash.display.MovieClip;
 
	public class Sample2 extends MovieClip{
 
		public function Sample2(){
			trace( "Document Class Sample1" );
		}
 
	}
}

コンストラクタでトレースを吐いている以外、何もしていません。
さて、この「クラスを書き出すフレーム」を2フレームに設定したflaファイルで、ドキュメントクラスを設定した場合、コンストラクタのトレースはいつ実行されるでしょうか?

確認してみると、一番最初。1フレームのフレームスクリプトでtraceしている「1:Frame Script Start」よりも前に実行されます。
そのドキュメントのコンストラクタってことだから、冷静に考えるとこれでいいような気もしますが、「クラスを書き出すフレーム」との整合性がわかりにくいです。しかも、このドキュメントクラスを設定すると、これまた正しく2フレームに移動しません…。
traceを確認すると

rslPreloader.addEventListener( RSLEvent.RSL_PROGRESS, onRslProgress );
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

のイベントのRSLEvent.RSL_LOAD_COMPLETEが戻ってきていないのでこれが原因のようなのですが、解決策的にはRSLEvent.RSL_PROGRESSの完了で判定してもダメで、(VerifyError: Error #1014: クラス flashx.textLayout.container::TextContainerManager が見つかりません。となる。)
今のところ解決策的にはドキュメントクラスを使わず、コンテナとなるインスタンスを2フレームで追加する方法。

こんな感じで、コンテナとなるクラスを作って、

package sample{
 
	import flash.display.Sprite;
	import share.PhotoTemplate;
 
	/** 2フレームで追加されコンテナとなるクラス。 */
	public class Container extends Sprite{
 
		public function Container(){
			var photo:PhotoTemplate = new PhotoTemplate();
			photo.x	= 250;
			photo.y	= 300;
			addChild( photo );
		}
	}
}

2フレームのフレームスクリプトで配置する。

import sample.Container;
 
var container = new Container();
addChild( container );

これだとうまくいっているけど…。僕はよくProgressionを使っているので、ドキュメントクラスを使えないのはいたいな。
やはり一筋なわではいかないか。RSL。

蛇足ですが、キャッシュされたSWZは、それぞれ以下にあります。

Windows 95/98/ME/2000/XP
C:¥Documents and Settings¥user_name¥Application Data¥Adobe¥Flash Player¥AssetCache¥

Windows Vista
C:¥Users¥user_name¥AppData¥Roaming¥Adobe¥Flash Player¥AssetCache¥

Linux
/home/user_name/.adobe/Flash_Player/AssetCache/

Mac OSX
/Users/user_name/Library/Cache/Adobe/Flash Player/AssetCache/

キャッシュを消して読込みを確認するには上記ディレクトリの中のファイルを削除します。
またSWZではなく、フェイルオーバー RSLを試すには、
Adobe – Flash Player : 設定マネージャー – グローバルストレージ設定パネル
で、共通のFlashコンポーネントを格納して、ダウンロード回数を削減しますのチェックを外したり、記憶容量を0にするなどすると確認出来ました。

“ランタイム共有ライブラリのさらなるローディング方法” に コメントはありません

コメントをどうぞ